C++添加sleep引发死锁崩溃问题及相关技术疑问
问题描述
我编写了如下线程启动代码:
std::thread t1(func1); std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); std::thread t2(func2); std::thread t3(func3); t1.join(); t2.join(); t3.join();
原本t1持有mutex m1的锁,t2等待获取该锁时运行正常,但在t1中添加5秒sleep后,程序崩溃并抛出错误:
terminate called after throwing an instance of 'std::system_error' what(): Resource deadlock avoided Aborted (core dumped)
回溯信息
线程1
(gdb) thread 1 #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 0x00007fa9eae827f1 in __GI_abort () at abort.c:79 #2 0x00007fa9eb875957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007fa9eb87bae6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007fa9eb87ab49 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007fa9eb87b4b8 in __gxx_personality_v0 () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007fa9eb243573 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1 #7 0x00007fa9eb243ad1 in _Unwind_RaiseException () from /lib/x86_64-linux-gnu/libgcc_s.so.1 #8 0x00007fa9eb87bd47 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #9 0x00007fa9eb877a23 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #10 0x000055d2f9bbe02f in std::unique_lock<std::mutex>::lock (this=0x55d2fbe062f8) at /usr/include/c++/7/bits/std_mutex.h:264
线程2
(gdb) thread 2 #0 0x00007fa9ebd7ed2d in __GI___pthread_timedjoin_ex (threadid=140367767017216, thread_return=0x0, abstime=0x0, block=<optimized out>) at pthread_join_common.c:89 #1 0x00007fa9eb8a6933 in std::thread::join() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #2 0x000055d2f9bb6e20 in main (argc=<optimized out>, argv=<optimized out>) at tests/benchmark-tests/benchmarkTest.cpp:294 (gdb)
线程3
(gdb) bt #0 0x00007fa9ebd87d50 in __GI___nanosleep (requested_time=requested_time@entry=0x7fa9eae40d30, remaining=remaining@entry=0x7fa9eae40d30) at ../sysdeps/unix/sysv/linux/nanosleep.c:28 #1 0x000055d2f9bbeaf5 in std::this_thread::sleep_for<long, std::ratio<1l, 1000l>> (__rtime=...) at /usr/include/c++/7/thread:373
疑问
- 程序为何崩溃?报错提示避免了死锁,但我无法理解死锁场景是什么?
- C++是否内置死锁检测机制?能否通过某种标志禁用该机制?
可复现完整代码
#include <iostream> #include <thread> #include <mutex> #include <unordered_map> class Worker { public: Worker() : workerLock(workerMutex, std::defer_lock) {} ~Worker() { workerLock.lock(); workerLock.unlock(); } void acquireWorkerLock() { workerLock.lock(); } void releaseWorkerLock() { workerLock.unlock(); } void work() { std::cout << "I'm working" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(10000)); } private: std::mutex workerMutex; std::unique_lock<std::mutex> workerLock; }; class WorkerManager { public: void newWorker(std::string name) { const std::lock_guard<std::mutex> lock(managerMutex); bool workerAlreadyPresent = ( workers.find( name ) != workers.end() ); if( !workerAlreadyPresent ) { Worker *worker = new Worker(); workers.insert( {name, worker} ); } } void work(std::string name) { std::unique_lock<std::mutex> lock(managerMutex); auto workerIt = workers.find( name ); if( workerIt != workers.end() ) { Worker *worker = workerIt->second; worker->acquireWorkerLock(); lock.unlock(); worker->work(); worker->releaseWorkerLock(); } } void removeWorker(std::string name) { std::unique_lock<std::mutex> lock(managerMutex); auto workerIt = workers.find( name ); if( workerIt != workers.end() ) { Worker *worker = workerIt->second; workers.erase(workerIt); lock.unlock(); delete worker; } } private: std::unordered_map<std::string, Worker*> workers; std::mutex managerMutex; }; WorkerManager wm; void func1() { wm.work("Foo"); std::cout << "Work done by worker Foo\n"; } void func2() { wm.removeWorker("Foo"); std::cout << "Worker removed Foo\n"; } void func3() { wm.newWorker("Bar"); std::cout << "Worker added Bar\n"; } int main() { wm.newWorker("Foo"); std::thread t1(func1); std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); std::thread t2(func2); std::thread t3(func3); t1.join(); t2.join(); t3.join(); return 0; }
对应完整回溯信息
线程1
(gdb) thread 1 #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 0x00007f79d03387f1 in __GI_abort () at abort.c:79 #2 0x00007f79d0bac957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007f79d0bb2ae6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007f79d0bb1b49 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007f79d0bb24b8 in __gxx_personality_v0 () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007f79d0918573 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1 #7 0x00007f79d0918ad1 in _Unwind_RaiseException () from /lib/x86_64-linux-gnu/libgcc_s.so.1 #8 0x00007f79d0bb2d47 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #9 0x00007f79d0baea23 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #10 0x00005653edd0f690 in std::unique_lock<std::mutex>::lock (this=0x5653eeb65e98) at /usr/include/c++/7/bits/std_mutex.h:264 #11 0x00005653edd0f048 in Worker::~Worker (this=0x5653eeb65e70, __in_chrg=<optimized out>) at test.cpp:10 #12 0x00005653edd0f443 in WorkerManager::removeWorker (this=0x5653edf16140 <wm>, name="Foo") at test.cpp:57
线程2
(gdb) thread 2 #0 0x00007f79d06f1d2d in __GI___pthread_timedjoin_ex (threadid=140161156749056, thread_return=0x0, abstime=0x0, block=<optimized out>) at pthread_join_common.c:89 #1 0x00007f79d0bdd933 in std::thread::join() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #2 0x00005653edd0eb0e in main () at test.cpp:89 (gdb)
线程3
(gdb) thread 3 #0 0x00007f79d06fad50 in __GI___nanosleep (requested_time=0x7f79cff58cb0, remaining=0x7f79cff58cb0) at ../sysdeps/unix/sysv/linux/nanosleep.c:28 #1 0x00005653edd0fb2d in std::this_thread::sleep_for<long, std::ratio<1l, 1000l>> (__rtime=...) at /usr/include/c++/7/thread:373 #2 0x00005653edd0f135 in Worker::work (this=0x5653eeb65e70) at test.cpp:21 #3 0x00005653edd0f339 in WorkerManager::work (this=0x5653edf16140 <wm>, name="Foo") at test.cpp:46
问题解答
1. 程序崩溃原因与死锁场景分析
从回溯信息和完整代码可以明确问题根源:
- 线程时序:t1启动后调用
wm.work("Foo"),获取了Foo的workerMutex锁,随后进入10秒的sleep;2秒后t2启动,调用wm.removeWorker("Foo"),在删除Foo对象时触发Worker的析构函数。 - 死锁触发点:
Worker的析构函数中尝试调用workerLock.lock(),但此时t1线程已经持有该workerMutex的锁且正在sleep,而t2线程试图再次获取同一锁。Linux下的pthread mutex默认是非递归锁,同一线程重复加锁会直接触发死锁检测,系统抛出Resource deadlock avoided错误终止程序。 - 这里的死锁是同一线程重复请求同一非递归锁导致的自我死锁,而非多线程循环等待的经典死锁。
2. C++的死锁检测机制与禁用方式
- 死锁检测来源:C标准本身没有强制要求内置死锁检测,但GCC的libstdc实现依赖Linux的pthread库,而pthread默认会检测同一线程对非递归锁的重复加锁操作,一旦检测到就会抛出错误。
- 禁用方式:如果要避免这种检测,可以将
std::mutex替换为递归锁std::recursive_mutex,它允许同一线程多次获取同一锁,不会触发死锁检测。注意递归锁需谨慎使用,容易掩盖代码逻辑问题。另外,没有全局开关可以直接禁用pthread的死锁检测,修改锁类型是最直接的解决方案。
内容的提问来源于stack exchange,提问作者VinayakaB
相关产品推荐
相关产品推荐

