销毁QThread时检测到数据竞争,请求确认是否为误报
多线程Qt项目Helgrind数据竞争排查
开发多线程Qt项目时,使用Helgrind(Valgrind工具)检测线程一致性问题,关闭应用后收到大量同类数据竞争报告,报错指向Qt框架内部的QObjectPrivate::ConnectionData::removeConnection()方法。
Helgrind报错详情
==50932== Possible data race during read of size 8 at 0x3413C718 by thread #26 ==50932== Locks held: none ==50932== at 0xB91ED11: QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0xB92A4E2: QObject::~QObject() (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0x525E9F: classA::~classA() (classA.cpp:52) ==50932== by 0x525EC3: classA::~classA() (classA.cpp:52) ==50932== by 0xB922012: QObject::event(QEvent*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0x9CC66D2: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/local/qt5/lib/libQt5Widgets.so.5.15.10) ==50932== by 0xB8F4E19: QCoreApplication::notifyInternal2(QObject*, QEvent*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0xB8F7F06: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0xB94EA36: ??? (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0xCCA8D3A: g_main_context_dispatch (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==50932== by 0xCCFE257: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==50932== by 0xCCA63E2: g_main_context_iteration (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==50932== ==50932== This conflicts with a previous write of size 8 by thread #25 ==50932== Locks held: none ==50932== at 0xB91ED20: QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0xB92A4E2: QObject::~QObject() (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0x468CA9: IclassB::~IclassB() (IclassB.h:43) ==50932== by 0x4646ED: classB::~classB() (classB.cpp:75) ==50932== by 0x464711: classB::~classB() (classB.cpp:75) ==50932== by 0xB922012: QObject::event(QEvent*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0x9CC66D2: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/local/qt5/lib/libQt5Widgets.so.5.15.10) ==50932== by 0xB8F4E19: QCoreApplication::notifyInternal2(QObject*, QEvent*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== Address 0x3413c718 is 8 bytes inside a block of size 88 alloc'd ==50932== at 0x484AFF5: operator new(unsigned long) (vg_replace_malloc.c:483) ==50932== by 0xB9233E4: QObjectPrivate::connectImpl(QObject const*, int, QObject const*, void**, QtPrivate::QSlotObjectBase*, Qt::ConnectionType, int const*, QMetaObject const*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0xB923964: QObject::connectImpl(QObject const*, void**, QObject const*, void**, QtPrivate::QSlotObjectBase*, Qt::ConnectionType, int const*, QMetaObject const*) (in /usr/local/qt5/lib/libQt5Core.so.5.15.10) ==50932== by 0x3E52AF: std::enable_if<QtPrivate::FunctionPointer<MainWindow::MainWindow(QWidget*)::{lambda()#31}>::ArgumentCount==(-1), QMetaObject::Connection>::type QObject::connect<void> (QObject::*)(QObject*), MainWindow::MainWindow(QWidget*)::{lambda()#31}>(QtPrivate::FunctionPointer<void> (QObject::*)(QObject*)>::Object const*, void (QObject::*)(QObject*), > QObject const*, MainWindow::MainWindow(QWidget*)::{lambda()#31}, > Qt::ConnectionType) (qobject.h:347) ==50932== by 0x3D2149: MainWindow::MainWindow(QWidget*) (mainwindow.cpp:633) ==50932== by 0x374017: main (main.cpp:199) ==50932== Block was alloc'd by thread #1 ==50932==
相关类与析构函数
IclassB是classB的空接口,无任何实现。classA和classB分别运行在不同线程,析构函数如下:
classB析构函数
classB::~classB() { if (d != nullptr) { delete d->member; delete d; } }
classA析构函数
classA::~classA() { if (d != nullptr) { if (d->mm->isActive()) d->mm->stop(); delete d; } }
问题分析与排查建议
- 并非必然是误报:Helgrind对Qt信号槽的内部同步逻辑可能存在误判,但大量同类报告更可能是代码中对象生命周期管理不当导致。
- Qt对象线程归属规则:QObject子类的析构必须在其所属线程(即
thread()返回的线程)中执行,否则极易引发跨线程数据竞争。从调用栈看,析构是通过事件循环触发的,但两个线程同时操作同一个ConnectionData块,说明可能存在跨线程销毁对象或未正确断开跨线程信号槽连接的情况。 - 具体排查方向:
- 检查
classA与classB之间的跨线程信号槽连接,关闭应用前是否提前调用disconnect()断开所有关联,或确保接收方线程的事件循环已完全退出。 - 强制所有QObject子类在自身所属线程内析构:比如用
moveToThread将对象绑定到对应线程,在线程退出前触发对象析构逻辑,避免跨线程销毁。 - 验证
d->mm的线程安全性:isActive()和stop()调用是否需要加锁同步,确保状态读取和修改的原子性。
- 检查
- 关于Qt内部方法:虽然报错指向Qt框架的
removeConnection(),但根源通常是上层代码的对象管理问题,而非Qt本身的BUG。
内容的提问来源于stack exchange,提问作者Dazckel
相关产品推荐
相关产品推荐

