C++ 多线程的错误和如何避免(7)
要以相同顺序获取多个锁
多线程在加锁解锁时,可能会出现死锁问题,比如,
线程 1 在加锁 mutex A 后,继续尝试获取 mutex B,而 mutex B 已经被线程 2 获取,而线程 2 在等待获取 mutex A,mutex B 只有线程 2 获取 mutex A 后才能解锁,
这就导致线程 1 和线程 2 互相等待锁,而这一操作就是死锁情况,容易导致程序挂起。

使用代码模拟死锁场景:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
using namespace std;
std::mutex muA;
std::mutex muB;
void CallHome_AB(string message)
{
muA.lock();
//Some additional processing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
muB.lock();
cout << "Thread " << this_thread::get_id() << " says " << message << endl;
muB.unlock();
muA.unlock();
}
void CallHome_BA(string message)
{
muB.lock();
//Some additional processing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
muA.lock();
cout << "Thread " << this_thread::get_id() << " says " << message << endl;
muA.unlock();
muB.unlock();
}
int main()
{
thread t1(CallHome_AB, "Hello from Jupiter");
thread t2(CallHome_BA, "Hello from Pluto");
t1.join();
t2.join();
return 0;
}
运行后,会发现控制台没有打印任何字符串,因为两个线程都尝试获取对方的锁,导致了死锁
如何解决?
最好的办法就是所有的锁都可以以相同的顺序获得,比如线程 1 顺序获得 mutex A 和 mutex B,再释放 mutex B 和 mutex A,线程 2 也顺序获得 mutex A 和 B,再顺序释放
std::mutex muA;
std::mutex muB;
void CallHome_AB(string message) {
muA.lock();
// Some additional processing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
muB.lock();
cout << "Thread " << this_thread::get_id() << " says " << message << endl;
muB.unlock();
muA.unlock();
}
void CallHome_BA(string message) {
muA.lock();
// Some additional processing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
muB.lock();
cout << "Thread " << this_thread::get_id() << " says " << message << endl;
muB.unlock();
muA.unlock();
}
当然目前也有封装好的 C++ 函数来同时获得两个锁,如下
std::scoped_lock lock{muA, muB};
The class scoped_lock is a mutex wrapper that provides a convenient RAII-style mechanism for owning one or more mutexes for the duration of a scoped block.
When a scoped_lock object is created, it attempts to take ownership of the mutexes it is given. When control leaves the scope in which the scoped_lock object was created, the scoped_lock is destructed and the mutexes are released. If several mutexes are given, deadlock avoidance algorithm is used as if by std::lock.
或者我们也可以用 std::timed_mutex 来解决这个问题,当超过一定时间后自动释放锁
The timed_mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.

浙公网安备 33010602011771号