You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

疑问

  1. 程序为何崩溃?报错提示避免了死锁,但我无法理解死锁场景是什么?
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:45:54