As you've pointed out, std::lock_guard
in itself doesn't provide a deadlock-free way of locking multiple mutexs. Without a safe method, you run into the Dining Philosophers Problem.
std::lock
implements a deadlock-free algorithm which locks multiple Lockable objects.
It can be used with
std::mutex m1, m2;
{ // Option A - lock mutexes first, adopt later
std::lock(m1, m2);
std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
// critical section ...
}
{ // Option B - defer first, lock locks later
std::unique_lock<std::mutex> lock1(m1, std::defer_lock);
std::unique_lock<std::mutex> lock2(m2, std::defer_lock);
std::lock(lock1, lock2);
// critical section ...
}
{ // Option C - std::scoped_lock (C++17, but provided here for completeness)
std::scoped_lock lock(m1, m2);
}
If you don't need the extra features that std::unique_lock
provides (e.g. transferring ownership of the lock elsewhere), then std::lock_guard
should be preferred.
Note: the examples show only two locks, but all methods work with arbitrarily many locks.