示例代码
#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行阻塞调用改为非阻塞调用文章来源:https://www.toymoban.com/news/detail-415168.html
QMetaObject::invokeMethod(this, [this](){ qDebug() << "invokeMethod:" << ++count_; });
方案二:在锁外调用(仅适用于无数据竞争的情况,或采用原子变量),即去掉第17行的加锁。文章来源地址https://www.toymoban.com/news/detail-415168.html
到了这里,关于【调试记录】QT中使用多线程导致的死锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!