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

时间:2014-08-12 14:22:39

标签: c++ multithreading qt

我有一个派生自QObjectUploadWorker的类,它已经使用推荐的在线程中运行任务的方式启动,如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()无法正常工作。

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

3 个答案:

答案 0 :(得分:7)

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

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

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对象,因此你需要保护每个访问/写入这个对象的部分。

答案 1 :(得分:3)

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();来运行事件,要么重新设计该工作,以便通过事件循环中的一系列调用来处理它。

答案 2 :(得分:0)

在Uploader类中标记插槽:void finishedWhenQueueIsEmpty();

然后你打电话: -

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

所以一个人就完成了......'和另一个完成时......'

如果您使用此连接语法(pre Qt 5.0),那么您可以检查connect调用的返回值,或者至少在调试器中运行时查看调试输出,它会告诉您插槽是无效。

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

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