多线程代码中 QSerialPort 的 Qt 错误:QCoreApplication::sendEvent:“无法将事件发送到不同线程拥有的对象

Posted

技术标签:

【中文标题】多线程代码中 QSerialPort 的 Qt 错误:QCoreApplication::sendEvent:“无法将事件发送到不同线程拥有的对象【英文标题】:Qt error with QSerialPort in multithreaded code: QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread 【发布时间】:2013-05-30 11:29:40 【问题描述】:

删除 serialPort 会在调试版本中导致此消息:

QCoreApplication::sendEvent 中的 ASSERT 失败:“无法向其他线程拥有的对象发送事件。当前线程 c0a528。接收器 ''(类型为 'QSerialPort')是在线程 c76850 中创建的”,文件 kernel\qcoreapplication.cpp ,第 532 行

当主窗口关闭时,~QSerialReader 中会触发实际错误。这是代码。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)

    runnerThread=new QThread(this);    
    /*  - connect thread start signal to qrunnerthread's runLoop()
        this starts the main job loop            
        - connect thread finished signal to mainwindows' deleteLater
        to ensure proper deletion */
    qDataReader = new QDataReader();
    connect(runnerThread, SIGNAL(started()), qRunnerThread, SLOT(runLoop()));    
    connect(runnerThread, SIGNAL(finished()), this, SLOT(deleteLater()));
    qDataReader->moveToThread(runnerThread);
    runnerThread.start();


MainWindow::~MainWindow()

    runnerThread->exit();


//slot that runs when thread is started
void QDataReader::runLoop() 
    /* this code runs in different thread than QDataReader */    
    //serialPort parent will be QThread (runnerThread)
    serialReader=new QSerialReader(this);        

    while(doStuff)     
        QString data=serialReader.readData();
        emit dataReceived(data);    
    


QDataReader::~QDataReader() 
    delete runner;


QSerialReader::QSerialReader(QObject* parent) 
    //serialPort parent will be QSerialReader
    serialPort = new QSerialPort(this);


QSerialReader::~QSerialReader()    

    /*
    deleting serialPort causes this message in debug builds:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread c0a528. Receiver '' (of type 'QSerialPort') was created in thread c76850", file kernel\qcoreapplication.cpp, line 532
    */
    delete serialPort;  

edit1:佩佩的建议:

如果我将 QSerialReader 更改为 QDataReader 中的成员,问题就消失了。但是使用 QSerialPort::open 打开端口时会出现此错误:

QObject:无法为不同线程中的父级创建子级。 (Parent 是 QSerialPort(0xdf6930),parent 的线程是 QThread(0xd8a528),当前线程是 QThread(0xdf6850) QObject:无法为不同线程中的父级创建子级。 (Parent是QSerialPort(0xdf6930),parent的线程是QThread(0xd8a528),当前线程是QThread(0xdf6850)

QSerialPort::open(xx) 在 serialReader.readData() 内部调用;

所以还是……一些讨厌的问题。几年前我用 Java 编码时没有遇到过这样的问题。

stack trace when CSerialReader::openPort is called. this calls QSerialPort::open. Clearly this is run in other thread than main.

0   CSerialReader::openPort serialreader.cpp    480 0xff7211    
1   CSerialReader::readData serialreader.cpp    209 0xff4e1d    
2   QDataReader::runLoop    qdatareader.cpp 138 0xffa3fe    
3   QDataReader::qt_static_metacall moc_qdatareader.cpp 62  0x1040824   
4   QMetaObject::activate   qobject.cpp 3539    0x669b0e14  
5   QThread::started    moc_qthread.cpp 113 0x66a29979  
6   QThreadPrivate::start   qthread_win.cpp 345 0x66816918  
7   _callthreadstartex  threadex.c  314 0xf71a293   
8   _threadstartex  threadex.c  297 0xf71a224   
9   BaseThreadInitThunk kernel32        0x74cd3677  
10  __RtlUserThreadStart    ntdll32     0x76f9c002  
11  _RtlUserThreadStart ntdll32     0x76f9bfd5  

创建 CSerialReader 时的堆栈跟踪(作为成员变量)。这是在主线程中调用的。

0   CSerialReader::CSerialReader    Runner.cpp  48  0x9d3d14    
1   QDataReader::QDataReader    qdatareader.cpp 10  0x9d8fa4    
2   MainWindow::MainWindow  mainwindow.cpp  72  0x9c61eb    
3   main    main.cpp    105 0x9c4adc    
4   WinMain qtmain_win.cpp  131 0xa273ba    
5   __tmainCRTStartup   crtexe.c    547 0xa269e0    
6   WinMainCRTStartup   crtexe.c    371 0xa2676f    
7   BaseThreadInitThunk kernel32        0x74cd3677  
8   __RtlUserThreadStart    ntdll32     0x76f9c002  
9   _RtlUserThreadStart ntdll32     0x76f9bfd5  

【问题讨论】:

【参考方案1】:
serialReader=new QSerialReader(this);

不要这样做。父母和孩子必须生活在同一个线程中。在您的情况下,父(QThread)生活在它自己的线程中,而孩子(QSerialReader)生活在您产生的线程中。不能直接在栈上分配吗?

【讨论】:

感谢您的回复!我稍后会试试。但是 runLoop() 在 runnerThread 关联中运行,对吗?所以'this'是一个指向runnerThread的指针。我认为没问题 peppe:我试过了,但现在出现了另一个错误。检查原帖。我添加了相关信息。 您可以在打印该警告的语句(Qt 内部)上放置一个断点,然后查看堆栈跟踪吗? 佩佩,好的,我找到了原因。 serialPort 是在 QSerialReader 类中使用 new 创建的,它在主线程中运行。 serialPort->open() 方法在通信开始时在其他线程中运行。显然 open() 创建了新对象,这就是出现错误的原因。我想这已经解决了。 作为结论,看起来 QSerialPort 类不打算在多线程环境中使用。

以上是关于多线程代码中 QSerialPort 的 Qt 错误:QCoreApplication::sendEvent:“无法将事件发送到不同线程拥有的对象的主要内容,如果未能解决你的问题,请参考以下文章

Qt5 QSerialPort 写入数据

Qt QSerialPort 读写

Qt QSerialport 未插入设备未关闭

Qt Widget 之简易串口助手(QSerialPort)

Qt Widget 之简易串口助手(QSerialPort)

Qt:使用多线程结束时报错的问题