示例代码
#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行的加锁。