Множественные блокировки с мьютексом и возможность взаимоблокировки

Я новичок в потоках и пытаюсь понять мьютекс.
Я понимаю мьютекс как некоторый объект ( ключ), который выбирается только одним потоком (если он выбран, то другие потоки не могут выбрать его и должны ждать), чтобы получить доступ к некоторой части кода, которую мы хотим заблокировать.
таким образом, только один поток имеет доступ к этой заблокированной части кода в то время ( например, общий счетчик ). Другие потоки будут ждать, пока мьютекс не будет разблокирован и так далее.

Mutex1.Lock();
 {
     Mutex2.Lock();
     {
          // Code locked by mutex 1 and 2.
     }
     Mutex2.Unlock();

     // Code locked by mutex 1.
 }
 Mutex1.Unlock();

что происходит если я напишу несколько блокировок мьютекса?
оба мьютексы попадаешь в той же ветке? Я также читал, что несколько блокировок мьютекса могут вызвать взаимоблокировку.
может ли кто-нибудь объяснить и предоставить мне пример того, как я могу вызвать тупик, заблокировав часть кода с 2 мьютексами?

3 ответов


поток может содержать несколько замков, да. И тупик действительно может произойти, даже если он приобрел только один мьютекс.. Посмотрите на рабочий процесс:

Поток A

. 
.
.
lock mutex1
.
<---- Context switch! ----->

Поток B

.
.
.
.
lock mutex2
.
.
.
try lock mutex1 ----> BLOCKED UNTIL THREAD A RELEASES LOCK!

Поток A

.
.
.
try lock mutex2 ---> BLOCKED UNTIL THREAD B RELEASES LOCK!

тупик!

читать этой, некоторые типы мьютексов, такие как PTHREAD_MUTEX_NORMAL может привести к тому, что поток сам заблокирует его.

.
.
.
lock mutex
.
.
.
try lock

вот безопасный способ кодировать то, что вы описываете:

std::mutex Mutex1;
std::mutex Mutex2;

void
test()
{
    using namespace std;
    lock(Mutex1, Mutex2);  // no deadlock!
    lock_guard<mutex> lk1{Mutex1, adopt_lock};
    {
        lock_guard<mutex> lk2{Mutex2, adopt_lock};
        // Code locked by mutex 1 and 2.
    }   // lk2.unlock()
    // Code locked by mutex 1.
}   // lk1.unlock()

этот код не может взаимоблокировки, потому что std::lock(Mutex1, Mutex2) блокирует оба мьютекса, избегая взаимоблокировки (по некоторому внутреннему алгоритму). Преимущество использования std::lock это то, что вам не нужно помнить, в каком порядке вам нужно заблокировать мьютексы (что упрощает обслуживание в большой базе кода). Но недостатком является то, что вам нужно запереть оба замка в одной точке в коде. Если вы не можете заблокировать их одновременно, затем вы должны вернуться к порядку, как описывают другие ответы.

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


что произойдет, если я пишу несколько блокировок mutex? Оба мьютексы выбрал той же теме?

поток может одновременно содержать любое количество мьютексов. В вашем примере кода поток, который входит в область, защищенную Mutex1 будет пытаться приобрести Mutex2, ожидая, когда другой поток освободит его первым, если это необходимо. В представленном вами коде нет особых причин думать, что это не удастся.

Я тоже читал что множественные блокировки мьютекса могут вызвать тупик. Может ли кто-нибудь объяснить мне, как я могу вызвать тупик блокировка части кода с 2 мьютексами?

предположим, что есть два потока, один держит mutex1, а другой придерживая mutex2 (в этом случае, по крайней мере, она должна чем-то отличаться от псевдокода). Теперь предположим, что каждый поток пытается получить другой мьютекс, не освобождая тот, который он уже содержит. Первый поток должен приобрести mutex2 для продолжайте, и не можете сделать этого, пока другой не освободит его. Второй поток должен получить мьютекс1 для продолжения и не может этого сделать, пока другой не освободит его. Ergo,ни нить когда-нибудь продолжить -- это тупик.

общее правило таково: если есть набор мьютексов, все из которых два или более потока могут каждый хотеть провести аналогично, то должен быть фиксированный относительный порядок тех мьютексов, которые каждый поток чтит при приобретении любое их подмножество.