Qt信号在槽断开连接后的处理方式

3

我有一串实时数据来自于一个生产者对象所处理的硬件。 这将会连接到一个消费者,以在其自己的线程中处理它,以保持GUI响应。

mainwindow::startProcessing(){
    QObject::connect(producer,SIGNAL(dataReady(data*),consumer,SLOT(doData(data*)));
    consumer->moveToThread(&consumerThread);        
    consumerThread.start(); 
}

mainwindow::stopProcessing(){
    producer->disconnect(SIGNAL(dataReady(data*));
    consumer->cleanup();                        
    delete consumer;
}

consumer::doData(data* x) {
    mutex.lock();
    processingObject stuff
    mutex.unlock()
}

consumer::cleanup() {
     mutex.tryLock();
      .... blah ....
      delete processingObject; // then doData gets called again
}

我的问题是,在销毁消费者对象后,即使进行了断开连接,我仍然会收到发往该对象的信号。我尝试了越来越复杂的互斥锁来阻止这种情况,但理想的解决方案是清理等待所有未处理的信号被处理完毕。
有没有办法监视排队等待槽的未处理信号数量?或者有没有方法清除它们?
3个回答

3
您似乎正在不同的线程中销毁消费者对象。如果消费者在正确的线程中进行销毁,QObject通常会处理所有断开连接和事件队列清除。从Qt文档中得知:
调用线程之外的QObject上的delete是不安全的(或以其他方式访问该对象),除非您保证该对象此时未处理事件。使用QObject::deleteLater()代替,将发布DeferredDelete事件,对象线程的事件循环最终将接收该事件。
只需将清理放在消费者的析构函数中,并使用 QMetaObject::invokeMethod(consumer, "deleteLater"); 使消费者能够从其自己的线程内部销毁自身。在槽上使用invokeMethod将以线程安全的方式发送调用deleteLater的调用,这似乎是必要的,因为我没有看到任何文档说deleteLater本身是线程安全的。如果您必须阻塞直到消费者被销毁,可以指定连接类型为 Qt :: BlockingQueuedConnection ,否则默认值 Qt :: AutoConnection 应该可以。

1
不幸的是,处理涉及到一个TCPSocket,它有一个定时器,而且你不能在等待定时器的线程上使用deleteLater()。 - Martin Beckett
我不太明白你的意思。消费者线程是否在 QAbstractSocket::waitFor* 上被阻塞了? - cgmb
当套接字连接时,我收到了关于deleteLater和QTimer的警告,我认为这一定是TCP套接字,因为线程中没有使用显式计时器。 - Martin Beckett
@MartinBeckett 你能发布一下警告文本吗? - cgmb

3
这里的问题是连接类型。您使用了默认的连接类型,因此它是Qt::AutoConnection。由于您连接了来自不同线程的对象,因此这将作为Qt::QueuedConnection运行。

现在我假设您的生产者比消费者更快地创建数据。这会导致在消费者线程的事件队列中缓冲数据。因此,当您断开信号时,您确实已经断开了,但是您有一堆数据在事件队列中等待您,给您一种仍然连接的感觉。

如何解决?使消费者更快或在消费者中添加某些标志,这将导致忽略传递到消费者插槽的数据。或使生产者变慢。尝试不同的通信模式。或使用并发API将作业拆分为多个线程。 最佳解决方案取决于您正在做什么的详细信息。

祝好运。


1

确保您不会多次连接信号,否则查看此邮件列表存档可能会给您一些提示/建议:

http://lists.trolltech.com/qt-interest/2000-05/thread00051-0.html

简而言之,也许可以尝试:
mainwindow::stopProcessing()
{
    // Block the producer's signals. 
    producer->blockSignals( true );

    // Perform clean up/stop
    consumer->cleanup();

    // Delete the consumer, this disconnects all signals connected 
    // to the consumer.
    delete consumer;

    // Restore the producer's signals
    producer->blockSignals( false );
}

编辑:修复了blockSignals调用,应该在生产者而不是消费者上。


谢谢,但似乎不能阻止在管道中的信号。 - Martin Beckett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接