Qt双定时器调用同一函数,加QMutex后出现死锁问题求助
解决Qt定时器调用加锁函数导致的死锁问题
嘿,我来帮你拆解下这个死锁的根源,以及对应的解决办法。
首先咱们得搞明白为什么会出现这种卡死的情况:
- 你的两个定时器触发的
timeout槽都是在主线程执行的,Qt的UI线程本身就是一个事件循环线程。 - 第一个定时器触发后进入
FunCal,QMutexLocker帮你拿到了锁,紧接着你启动了一个局部的QEventLoop并调用exec()——这个操作会临时接管主线程的事件处理逻辑,也就是说主线程现在会在这个局部循环里处理所有待办事件,包括第二个定时器的timeout事件。 - 这时候第二个定时器的事件被触发,再次进入
FunCal,尝试获取同一个互斥锁,但此时锁已经被主线程自己持有了(第一个FunCal还没执行完,锁没释放)。而QMutex是非递归锁,同一个线程重复获取会直接阻塞,于是主线程就卡在了第二个FunCal的锁等待环节,第一个FunCal的局部事件循环因为主线程被卡死,永远没法退出,锁也永远不会释放,死锁就这么产生了。
解决方案1:使用递归互斥锁(快速修复)
最简单的办法就是把普通的QMutex换成QRecursiveMutex,它允许同一个线程多次获取锁,不会造成线程内阻塞。
修改你的成员变量定义:
// 把原来的 QMutex _mtx; 替换成 QRecursiveMutex _mtx;
其他代码保持不变,再运行就能得到你预期的输出:
log in fun... getted lock in fun... log out fun... log in fun... getted lock in fun... log out fun...
解决方案2:避免在持有锁时启动事件循环(更优实践)
递归锁虽然能快速解决问题,但本质上是规避了设计上的小瑕疵——在持有锁的情况下启动事件循环本来就容易引发这类线程内的阻塞问题。更优雅的做法是把需要加锁和休眠的操作放到子线程里执行:
- 先创建一个工作类,把
FunCal的逻辑迁移进去:
class Worker : public QObject { Q_OBJECT public slots: void doWork() { qDebug()<<"log in fun..."; QMutexLocker loc(&_mtx); qDebug()<<"getted lock in fun..."; // 用QThread的休眠替代事件循环,避免阻塞主线程 QThread::msleep(100); qDebug()<<"log out fun..."; } private: QMutex _mtx; };
- 在
MainWindow里把工作对象放到独立子线程运行:
void MainWindow::Start() { QThread* workerThread = new QThread(this); Worker* worker = new Worker(); worker->moveToThread(workerThread); workerThread->start(); pTimer = new QTimer(this); pTimer->setInterval(100); connect(pTimer, SIGNAL(timeout()), worker, SLOT(doWork())); pTimer->start(); pTimer2 = new QTimer(this); pTimer2->setInterval(100); connect(pTimer2, SIGNAL(timeout()), worker, SLOT(doWork())); pTimer2->start(); }
这样所有加锁和休眠操作都在子线程里执行,主线程的事件循环不会被阻塞,定时器触发也不会互相干扰,从根源上避免了死锁问题。
内容的提问来源于stack exchange,提问作者danny
相关产品推荐
相关产品推荐

