Qt 4.8 在 moveToThread() 之后未调用的信号/插槽

Qt 4.8 Signals/Slots not called after moveToThread()

本文关键字:调用 信号 插槽 之后 moveToThread Qt      更新时间:2023-10-16

我有一个派生自QObject的类,UploadWorker,它已经使用Qt文档中演示的推荐的在线程中运行任务的方式启动。

QThread*      thread = new QThread();
UploadWorker* worker = new UploadWorker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));

现在,这工作得很好。然后,我的UploadWorker尝试使用相同的技术为自己启动一个名为Uploader的工人。

以下是标头的必要部分。

class UploadWorker : public QObject
{
    Q_OBJECT
public:
    // stuff
public slots:
    void doWork();
signals:
    void allWorkDone();
protected:
    void startUploader();
};
class Uploader : public QObject
{
    Q_OBJECT
public:
    // stuff
public slots:
    void doWork();
    void finishWhenQueueIsEmpty();
}; 

这是UploadWorker的实现。

void
UploadWorker::doWork()
{
    // This method is called when QThread emits started()
    // Prepare the upload thread
    startUploader();
    // Do some important work...
    // Notify the upload thread that we're done
    emit allWorkDone();
}
void
UploadWorker::startUploader()
{
    QThread*  thread = new QThread();
    Uploader* uploader = new Uploader();
    uploader->moveToThread(thread);
    connect(this,     SIGNAL(stopped()),   uploader, SLOT(stop()));
    connect(uploader, SIGNAL(finished()),  thread,   SLOT(quit()));
    connect(thread,   SIGNAL(finished()),  thread,   SLOT(deleteLater()));
    connect(uploader, SIGNAL(finished()),  uploader, SLOT(deleteLater()));
    connect(thread,   SIGNAL(started()),   uploader, SLOT(doWork()));
    connect(this,     SIGNAL(allWorkDone()), 
            uploader, SLOT(finishWhenQueueIsEmpty()));
    thread->start();
}

我现在的问题是,Uploader::finishWhenQueueIsEmpty()槽永远不会被触发。除非:我删除uploader->moveToThread(thread);.

错误在哪里?

编辑我忘了提到线程启动时调用Uploader::doWork()方法。在此示例中,它是任何其他不起作用的插槽finishWhenQueueIsEmpty()

我修复了有关插槽名称的拼写错误。

一种非常可能的可能性是,您正在执行Uploader::doWork()槽,并通过某种同步机制(例如布尔isRunning(等待对时隙Uploader::finishWhenQueueIsEmpty()的调用来完成其执行。

然后,您需要做的是像下面这样连接插槽:

connect(this,     SIGNAL(allWorkDone()), 
        uploader, SLOT(finishWhenQueueIsEmpty()),
        Qt::DirectConnection);

finishWhenQueueIsEmpty()使用互斥锁保护同步机制。

为什么?

因为在不同线程中的对象之间建立连接的默认模式是 Qt::QueuedConnection 。它在这里所做的是,这里的 Uploader 类的每个插槽都排队等待执行,也就是说,一旦线程返回到事件循环(即当前插槽完成运行(,队列中的下一个插槽就会运行。然后,这意味着在这种模式下,调用Uploader::doWork()然后调用Uploader::finishWhenQueueIsEmpty()将导致程序卡在doWork()中。

使用Qt::DirectConnection,插槽将在运行UploaderWorker::doWork()的线程中运行,并且实际上可以在Uploader::doWork()运行时运行。但是,您有两个线程可以访问同一个Uploader对象,因此您需要保护对该对象部分的每次访问/写入。

void
UploadWorker::doWork()
{
    // This method is called when QThread emits started()
    // Prepare the upload thread
    startUploader();
    // Do some important work...
    // Notify the upload thread that we're done
    emit allWorkDone();
}

这里的结构告诉我,在你实际完成"重要工作"之前,你不会让事件循环运行,这意味着不会触发任何插槽。

要么通过定期调用QApplication::processEvents();来运行事件,要么重新设计工作,以便由事件循环中的一系列调用来处理它。

在 Uploader 类中,您标记了插槽:void doneWhenQueueIsEmpty((;

然后你打电话:-

connect(this, SIGNAL(allWorkDone()), uploader, SLOT(finishWhenQueueIsEmpty()));

所以一个是"完成何时..."另一个"完成时间"。

如果您使用此连接语法(Qt 5.0 之前(,那么您可以检查连接调用的返回值,或者至少在调试器中运行时查看调试输出,这将告诉您该槽无效。

使用Qt 5连接语法,可以在编译时捕获:

connect(this, &UploadWorker::allWorkDone() uploader, &Uploader::finishWhenQueueIsEmpty());