unique_lock与普通mutex是否会引发死锁?附代码咨询
多线程代码死锁风险分析
核心问题解答:工作线程持mutex2时主线程锁mutex1不会死锁
你担心的场景不会引发死锁——mutex1和mutex2是完全独立的两个互斥量:
- 工作线程持有的是mutex2,它在执行
task时只会请求mutex1,不会再尝试获取mutex2; - 主线程在
task里只会操作mutex1,不会请求mutex2;
两者不存在循环等待对方持有的锁的死锁必要条件,所以这部分完全安全。
其他位置的潜在问题(无死锁,但有风险)
你的代码整体不存在死锁,但有几个需要注意的问题:
1. task函数中mutex1的手动解锁存在异常安全风险
task里直接调用mutex1.lock()和mutex1.unlock(),如果buffer++或std::cout操作抛出异常(虽然概率低,但C++标准允许流操作抛异常),mutex1会被永久锁定,导致所有调用task的线程永久阻塞——这不是死锁,但后果和死锁类似。
解决方法:用std::lock_guard或std::unique_lock自动管理锁,确保无论是否抛异常都能解锁:
void task(std::string threadName, int loopmax) { for(int i = 0; i < loopmax; i++) { std::lock_guard<std::mutex> lock(mutex1); // 构造时锁,析构时自动解锁 buffer++; std::cout << threadName << buffer << "\n"; } }
2. 全局变量notify存在数据竞争
工作线程写notify = true,主线程读if(!notify),两者没有同步机制,属于数据竞争,会导致未定义行为——比如主线程可能看不到notify的更新,进入不必要的cv.wait(),或者读取到半更新的值。
解决方法:要么用互斥量保护notify的访问,要么把notify改成原子类型:
std::atomic<bool> notify(false);
3. mutex2的使用逻辑是正常同步,无死锁
工作线程一开始就锁住mutex2,直到线程结束才释放;主线程在执行完两次task后尝试锁mutex2,此时如果工作线程还在运行,主线程会阻塞到mutex2被释放。这是正常的线程同步逻辑,不存在循环等待,不会死锁。
至于你提到的“主线程锁mutex2后调用task,线程不再交互”:因为工作线程被mutex2锁住无法继续执行,只有主线程在操作mutex1,所以自然看不到线程交互,这是执行顺序问题,不是死锁。
总结
你的代码不存在死锁风险,但有异常安全和数据竞争的问题,建议按照上面的方法修正。
内容的提问来源于stack exchange,提问作者user28518792
相关产品推荐
相关产品推荐

