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

C++定时器回调中互斥锁死锁问题求助

C++定时器回调中互斥锁死锁问题求助

首先得帮你理清死锁的根源:你现在的代码里,定时器线程在mainLoop中持有std::unique_lock的情况下直接执行了timeoutHandler()回调,而回调里又调用了stop()——stop()会尝试获取同一个互斥锁的lock_guard。这就导致了:

  • 主线程调用resetCallback后进入internalQuit,加锁设置状态后调用thread.join(),阻塞等待定时器线程结束;
  • 定时器线程此时拿着互斥锁,卡在回调里的stop()请求锁的步骤;
  • 两边互相等待:主线程等线程结束,线程等锁释放,死锁就这么发生了。

接下来给你几个可行的解决方案,按推荐程度排序:

方案一:执行回调前释放互斥锁(最优解)

核心思路是:让回调在无锁状态下执行,这样回调里调用任何需要加锁的公共方法时,都能顺利获取锁。

修改你的mainLoop逻辑,在执行timeoutHandler()之前主动解锁,执行完回调后再重新加锁,同时每次重新加锁后都检查退出状态(防止回调里已经触发了停止逻辑):

void mainLoop(Function &&timeoutHandler)
{
    while(true)
    {
        std::unique_lock lock{mutex};
        
        // 等待running状态或退出信号
        sleepCv.wait(lock, [this]{ return running || quit; });
        if(quit) break;

        // 等待初始定时到期
        auto initDeadline = std::chrono::steady_clock::now() + initTime;
        // 如果wait_until返回false,说明定时到期(不是被唤醒)
        if(!sleepCv.wait_until(lock, initDeadline, [this]{ return !running || quit; }))
        {
            // 执行回调前解锁
            lock.unlock();
            timeoutHandler();
            // 回调执行完重新加锁,检查是否需要退出
            lock.lock();
            if(quit) break;
        }
        if(!running || quit) break;

        // 处理间隔定时循环
        while(true)
        {
            auto intervalDeadline = std::chrono::steady_clock::now() + intervalTime;
            if(!sleepCv.wait_until(lock, intervalDeadline, [this]{ return !running || quit; }))
            {
                lock.unlock();
                timeoutHandler();
                lock.lock();
                if(quit) break;
            }
            if(!running || quit) break;
        }
        if(quit) break;
    }
}

这样修改后,当回调里调用stop()时,互斥锁处于未被持有的状态,stop()能顺利加锁设置running=false并通知条件变量,定时器线程在回调结束后重新加锁,会立刻检测到退出信号并终止循环,主线程的join()也能正常完成,死锁问题就解决了。

方案二:使用递归互斥锁(不推荐,治标不治本)

如果你不想大改逻辑,可以把std::mutex换成std::recursive_mutex——它允许同一个线程多次获取锁。这样回调里的stop()请求锁时,因为是同一个线程持有锁,不会阻塞。

但这种方案只是绕过了死锁,并没有解决根本问题:锁的持有时间会被拉长(从定时逻辑一直到回调执行完),可能引发其他潜在的线程安全问题,而且递归锁的性能也比普通互斥锁稍差,所以只适合临时应急,不推荐作为长期解决方案。

方案三:优化锁的粒度(进阶)

你可以把需要保护的变量拆分,比如将running、quit这类状态变量改成std::atomic<bool>,减少互斥锁的使用范围。不过条件变量的wait必须和互斥锁配合,所以这种方案需要调整的逻辑较多,适合对性能要求极高的场景。

对你之前尝试的补充

你之前尝试用std::move转移线程的思路其实解决不了死锁问题,因为死锁的根源是线程持有锁时调用回调,和线程的所有权转移无关,所以这个方向是走不通的。

最后,修改完代码后你的测试用例就能正常运行了:回调里的stop()能顺利执行,定时器会停止,第二个断言也能通过。

备注:内容来源于stack exchange,提问作者Deedss

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 15:27:42