Одновременное удержание двух блокировок мьютекса

Я хотел бы знать, возникнут ли какие-либо проблемы, если я одновременно удержу два boost::scoped_locks. Блокировки блокируют разные мьютексы. Рассмотрим следующий пример:

void foo1()
{
   boost::recursive_mutex::scoped_lock lock(mutex1);
   foo2();
}

void foo2()
{
   boost::recursive_mutex::scoped_lock lock(mutex2);
}

Я знаю, что это не должно вызывать тупиковую ситуацию. Но есть ли другие проблемы. Может быть, это может привести к тому, что поток будет спать слишком долго?


person Petko Six    schedule 20.07.2016    source источник


Ответы (2)


Удерживание нескольких замков само по себе не является проблемой.

Проблемы возникают, когда другие потоки пытаются получить те же самые блокировки в другом порядке, и в итоге вы получаете ABBA взаимоблокировок. Поток 1 блокирует A и B, затем поток 2 хочет заблокировать B, затем A, и оба в конечном итоге блокируются (если блокировка чередуется, поэтому t1 блокирует A, затем t2 блокирует B, а затем оба блокируются, пытаясь заблокировать другой) в ожидании другого снять одну из блокировок, чтобы иметь возможность продолжить (и снять свои собственные блокировки, которые позволят продолжить работу другой).

Итак, общее правило таково; если вам нужно получить более одной блокировки, убедитесь, что все потоки всегда пытаются получить эти блокировки в одном и том же порядке.

person Jesper Juhl    schedule 20.07.2016
comment
Спасибо за ваш ответ! :) - person Petko Six; 20.07.2016
comment
Просто продолжу: один из способов обеспечить порядок блокировки - это иметь легкие обертки вокруг мьютексов/блокировок, а затем заставить конструктор слоя i взять const ref полученной блокировки слоя (i-1). Тогда вы не можете построить его по-другому. Или - используйте C++17 и заблокируйте их одновременно. - person lorro; 20.07.2016
comment
@lorro Я хорошо знаю, что есть много способов решить эту проблему. Но я не пытался дать совет о том, как решить проблему с точки зрения реализации, просто указал в чем потенциальная проблема и общее правило, позволяющее ее избежать. Реализация оставлена ​​в качестве упражнения для читателя. :-) - person Jesper Juhl; 21.07.2016

Это может вызвать взаимоблокировку, если кто-либо захватит оба мьютекса в противоположном порядке.

void bar1() {
    boost::recursive_mutex::scoped_lock lock(mutex2);
    bar2();
}

void bar2() {
    boost::recursive_mutex::scoped_lock lock(mutex1);
}

Два потока, чередующиеся следующим образом, заблокируются:

                                 mutex1  mutex2
Time    Thread A     Thread B    owner   owner
  0     foo1()                     A
  1                  bar1()        A       B
  2                  bar2()        A       B
  3     foo2()                     A       B

В этот момент потоки A и B заблокированы. Я не думаю, что boost обеспечивает защиту от этого, хотя базовая ОС может.

person D.Shawley    schedule 20.07.2016
comment
Спасибо за ваш ответ! :) - person Petko Six; 20.07.2016