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

Rob*_*ohr 4 c++ qt multithreading

我从派生的类QObject,UploadWorker使用的Qt文档中示范运行中的线程任务的推荐方式,已启动.

QThread*      thread = new QThread();
UploadWorker* worker = new UploadWorker();

worker->moveToThread(thread);

connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
Run Code Online (Sandbox Code Playgroud)

现在,这完全正常.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();
}; 
Run Code Online (Sandbox Code Playgroud)

这是执行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();
}
Run Code Online (Sandbox Code Playgroud)

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

错误在哪里?

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

我修改了关于插槽名称的拼写错误.

JBL*_*JBL 8

一个非常可能的可能性是你正在执行你的Uploader::doWork()插槽并等待调用插槽Uploader::finishWhenQueueIsEmpty()来完成它的执行,通过一些同步机制(isRunning例如一个布尔值).

您需要做的是连接您的插槽,如下所示:

connect(this,     SIGNAL(allWorkDone()), 
        uploader, SLOT(finishWhenQueueIsEmpty()),
        Qt::DirectConnection);
Run Code Online (Sandbox Code Playgroud)

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

为什么?

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

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