Concurrency and parallelism
CP.21
Используйте `std::lock()` или `std::scoped_lock` для захвата нескольких `mutex`ов
Причина
Для избежания deadlocks на нескольких mutexах.
Пример
Это просит deadlock:
// thread 1
lock_guard<mutex> lck1(m1);
lock_guard<mutex> lck2(m2);
// thread 2
lock_guard<mutex> lck2(m2);
lock_guard<mutex> lck1(m1);
Вместо этого используйте lock():
// thread 1
lock(m1, m2);
lock_guard<mutex> lck1(m1, adopt_lock);
lock_guard<mutex> lck2(m2, adopt_lock);
// thread 2
lock(m2, m1);
lock_guard<mutex> lck2(m2, adopt_lock);
lock_guard<mutex> lck1(m1, adopt_lock);
или (лучше, но только C++17):
// thread 1
scoped_lock<mutex, mutex> lck1(m1, m2);
// thread 2
scoped_lock<mutex, mutex> lck2(m2, m1);
Здесь авторы thread1 и thread2 всё ещё не согласны с порядком mutexов, но порядок больше не имеет значения.
Примечание
В реальном коде mutexы редко названы так, чтобы удобно напомнить программисту о предполагаемом отношении и предполагаемом порядке захвата. В реальном коде mutexы не всегда удобно захватываются на последовательных строках.
Примечание
В C++17 можно просто написать
lock_guard lck1(m1, adopt_lock);
и тип mutex будет выведен.
Применение
Обнаружьте захват нескольких mutexов. Это неразрешимо в целом, но поймать общие простые примеры (как приведённый выше) легко.