Qt跨线程事件发送引发QCoreApplication::sendEvent断言失败原因咨询
首先,咱们先把核心错误的本质说清楚:你遇到的ASSERT failure in QCoreApplication::sendEvent是Qt最严格的线程安全机制在报警——任何Qt对象只能在它被创建的线程中接收事件。从错误日志和调试信息能明确看到:
- 现在尝试发送事件的是
DUMMY_LOOP线程(ID0x20e51b65eb0) - 接收事件的
gw2g对象是在主线程(ID0x20e4fd0c930)创建的
结合你的代码和调试细节,我梳理了几个高概率触发这个问题的场景,以及对应的排查和解决思路:
一、可能的触发原因
1. selectionModel的信号被跨线程触发
你在addExtenders()里连接了ui->listView->selectionModel()的selectionChanged信号到gw2g的槽。正常情况下,selectionModel和listView一起在主线程创建,它的信号应该在主线程发射,但如果出现以下情况就会跨线程触发:
- 你在
DUMMY_LOOP线程中直接操作了listView的模型或选择状态(比如调用setCurrentIndex、直接修改模型数据),这会导致selectionModel在DUMMY_LOOP线程发射信号。此时Qt::AutoConnection会尝试跨线程给gw2g发事件,一旦线程亲和性出现混乱,就会触发ASSERT。 - 哪怕你没有直接操作,模型的异步更新也可能出问题:如果模型数据是在
DUMMY_LOOP线程中修改的,且没有遵循QAbstractItemModel的线程安全规范(比如没用到beginInsertRows/endInsertRows这类线程安全的修改接口),也会间接触发选择信号跨线程发射。
2. dummyMain_c意外销毁导致线程亲和性混乱
你提到dummyMain_c有时会意外销毁,如果它是DUMMY_LOOP线程的管理对象,它的提前销毁会引发连锁问题:
- 线程事件循环可能异常终止,Qt内部的资源清理逻辑可能误把
gw2g的线程归属改成已销毁的DUMMY_LOOP线程; - 如果销毁时没有等待线程完全结束,残留的线程会尝试给已属于主线程的对象发送事件,直接触发ASSERT。
3. extend_list信号的异步操作埋下隐患
虽然你说还没执行到wait_xcon.exec(),但emit extend_list();已经触发了异步操作。如果这个信号连接到了DUMMY_LOOP线程的槽函数,而槽函数直接调用了gw2g的方法(不是通过信号槽),就会导致在非主线程操作gw2g对象,进而触发跨线程事件发送错误。
4. 事件循环重复启动的间接影响
QCoreApplication::exec: The event loop is already running这个提示说明某个线程里重复启动了事件循环。哪怕你没执行到wait_xcon.exec(),也可能是dummyMain_c的初始化代码里已经启动了一个事件循环,后续的循环启动失败会导致线程逻辑混乱,间接引发跨线程事件错误。
二、排查与解决建议
1. 严格管控跨线程UI/模型操作
确保所有对ui->listView、其模型、selectionModel的操作都在主线程执行。如果DUMMY_LOOP线程需要修改模型,必须用**信号槽(指定Qt::QueuedConnection)**或者QMetaObject::invokeMethod异步调用主线程方法,绝对不能直接操作:
// 错误示例:在DUMMY_LOOP线程直接修改模型 model->setData(index, newValue); // 正确示例:异步调用主线程方法 QMetaObject::invokeMethod(model, "updateModelData", Qt::QueuedConnection, Q_ARG(QModelIndex, index), Q_ARG(QVariant, newValue));
2. 确认gw2g的线程亲和性稳定
在gw2g的构造函数和可能修改线程的地方(比如moveToThread)添加日志,跟踪它的线程归属:
gw2g::gw2g(QObject *parent) : QObject(parent) { qDebug() << "gw2g created in thread:" << QThread::currentThreadId(); } // 在错误触发前的关键位置添加日志 qDebug() << "gw2g current thread:" << gw2g->thread()->threadId();
确保它始终属于主线程,没有被意外移到其他线程。
3. 修复dummyMain_c的销毁逻辑
确保dummyMain_c在DUMMY_LOOP线程完全结束后再销毁,销毁前必须等待线程终止:
dummyMain_c::~dummyMain_c() { if (m_dummyThread) { m_dummyThread->quit(); m_dummyThread->wait(); // 必须等待线程完全结束 delete m_dummyThread; } }
同时检查dummyMain_c的生命周期(比如是否是智能指针管理),避免它被意外销毁。
4. 检查extend_list信号的连接
查看extend_list连接的所有槽函数,确认这些槽函数是否在DUMMY_LOOP线程执行,且是否直接操作了主线程对象。如果是,改成Qt::QueuedConnection或者用invokeMethod异步调用。
5. 定位事件循环重复启动的根源
在所有调用exec()的地方添加日志,打印当前线程ID,找到重复启动的源头:
qDebug() << "Starting event loop in thread:" << QThread::currentThreadId(); wait_xcon.exec();
这样就能明确是不是DUMMY_LOOP线程里已经有一个事件循环在运行,导致后续启动失败。
内容的提问来源于stack exchange,提问作者dasmanfred

