> 文章列表 > 【调试记录】QT中使用多线程导致的死锁

【调试记录】QT中使用多线程导致的死锁

【调试记录】QT中使用多线程导致的死锁

示例代码

#include <QApplication>
#include <QWidget>
#include <QTimer>
#include <thread>
#include <mutex>using namespace std::chrono_literals;class Window : public QWidget{
public:Window(QWidget *parent = nullptr): QWidget(parent), count_{} {// 启动工作线程thd_ = std::jthread([this](std::stop_token st) {while(!st.stop_requested()) {std::this_thread::sleep_for(100ms);std::lock_guard lg(mtx_);QMetaObject::invokeMethod(this, [this](){ qDebug() << "invokeMethod:" << ++count_; }, Qt::BlockingQueuedConnection);}});// 定时器auto* timer{ new QTimer(this) };connect(timer, &QTimer::timeout, this, [this]() {std::lock_guard lg(mtx_);qDebug() << "timeout:" << ++count_;});timer->start(40);}~Window() = default;private:size_t count_;std::mutex mtx_;std::jthread thd_;
};int main(int argc, char *argv[]) {QApplication a(argc, argv);Window w;w.show();return a.exec();
}

运行结果

invokeMethod: 1
timeout: 2
timeout: 3
timeout: 4
invokeMethod: 5
timeout: 6
timeout: 7

原因分析

原因在于第18行采用阻塞队列的连接方式。

QMetaObject::invokeMethod(this, [this](){ qDebug() << "invokeMethod:" << ++count_; }, Qt::BlockingQueuedConnection);

子线程在第17行获取到锁,主线程刚好运行到24行准备获取锁。此时子线程执行第18行,阻塞调用等待主线程执行qDebug() << "invokeMethod:" << ++count_;完成。
子线程已经获取到锁,主线程等待获取锁,子线程又等待主线程事件循环执行函数,由此产生死锁

解决方案

方案一:将18行阻塞调用改为非阻塞调用

QMetaObject::invokeMethod(this, [this](){ qDebug() << "invokeMethod:" << ++count_; });

方案二:在锁外调用(仅适用于无数据竞争的情况,或采用原子变量),即去掉第17行的加锁。