Std :: Lock избегает тупиков, но эта программа застревает - PullRequest
6 голосов
/ 21 ноября 2019

Все, ссылаясь на вопрос в std :: lock по-прежнему вызывал взаимоблокировку

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

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

std::mutex m1;
std::mutex m2;

void func1()
{
    std::unique_lock<std::mutex> lock1(m1, std::defer_lock);
    printf("func1 lock m1\n");
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::unique_lock<std::mutex> lock2(m2, std::defer_lock);
    printf("func1 lock m2\n");
    std::lock(m1, m2);
    printf("func1 std lock\n");

}

void func2()
{
    std::unique_lock<std::mutex> lock1(m2, std::defer_lock);
    printf("func2 lock m2\n");
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::unique_lock<std::mutex> lock2(m1, std::defer_lock);
    printf("func2 lock m1\n");
    std::lock(m1, m2);
    printf("func2 std lock\n");
}



int main(int argc,char* argv[])
{
    std::thread th1(func1);
    std::thread th2(func2);
    th1.join();
    th2.join();
    return 0;
}

Выводится на экран: Блокировка func1 m1
Блокировка func2 m2
Блокировка func1 m2
Блокировка func1 std
Блокировка func2 m1
----- Здесь вывешены.

Почему func2 не продолжает работу, даже если func1 освободил оба мьютекса?

1 Ответ

12 голосов
/ 21 ноября 2019

Вместо:

std::lock(m1, m2);

используйте:

std::lock(lock1, lock2);

Более подробную информацию (включая пример) можно найти на странице справки для std::lock.

Почему ваш код зависает?

Когда вы вызываете std::lock(m1, m2), два мьютекса блокируются напрямую. Ни один из std::unique_lock s (lock1 и lock2) не знает об этом, и поэтому они не могут разблокировать мьютексы.

Таким образом, когда func1 заканчивается, оба мьютекса все еще заблокированы, иfunc2 не может проходить после строки std::lock(m1, m2).

Почему работает фиксированный код?

При вызове std::lock(lock1, lock2), std::unique_lock s (lock1 и * 1030)*) знают об этом - теперь они владеют замками и несут ответственность за их разблокировку (что происходит, когда они выходят из области видимости).

Итак, когда func1 заканчивается, оба мьютекса разблокируются, и func2 может проходить мимо линии std::lock(lock1, lock2). И все хорошо.

...