Ответ 1
1) Первый пример кода
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl;
}
Это стандартная защита блокировки, когда область действия завершена, блокируется lk
{
std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock);
std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
std::lock(lk1, lk2);
std::cout << e1.id << " and " << e2.id << " got locks" << std::endl;
// ...
}
Здесь мы сначала создаем блокировки, не приобретая их (что точка std::defer_lock
), а затем, используя std::lock
на обеих блокировках одновременно гарантирует, что они будут получены без риска возникновения взаимоблокировки, если другой вызывающий элемент функции перемежается (у нас может быть тупик, если вы замените он с двумя последовательными вызовами std::lock
:
{
std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock);
std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
std::lock(lk1);
std::lock(lk2); // Risk of dedalock !
std::cout << e1.id << " and " << e2.id << " got locks" << std::endl;
// ...
}
2) Второй пример кода
void swap(X& lhs, X&rhs){
if(&lhs == &rhs)
return;
// m is the std::mutex field
std::lock(lhs.m, rhs.m);
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
swap(lhs.some_detail, rhs.some_detail);
}
Теперь мы сначала получаем блокировки (все еще избегаем взаимоблокировок), а затем создаем блокирующие блоки, чтобы убедиться, что они правильно выпущены.
Обратите внимание, что std::adopt_lock
требует, чтобы текущий поток владел мьютексом (это так, потому что мы просто заблокировали их)
Заключение
Здесь есть 2 шаблона:
1) Заблокируйте оба мьютекса одновременно, затем создайте защитные устройства
2) Создайте защитные устройства, затем заблокируйте оба мьютекса одновременно
Оба шаблона эквивалентны и нацелены на одно и то же: безопасно заблокировать два мьютекса одновременно и обеспечить, чтобы разблокировка всегда возникала для обоих.
Что касается разницы между std::lock_guard
и std::unique_lock
, вы должны увидеть эту другую запись SO, большую часть времени std::lock_guard
достаточно.