2013-07-26 98 views
1

我有一個類應該在一個線程中運行,並且需要一個用於插槽的事件循環,目前我用moveToThread()很好地運行它,但我想使用QThreadPool,並且遇到了問題。在QThreadPool中執行插槽

QThreadPool我可運行的run()方法是從一個線程池(我檢查這與QThread::currentThread())呼籲運行,但我的插槽不彙集線程中運行的,所以我覺得對象沒有移動到池中的線程。

我認爲這是因爲我知道插槽在接收器的線程中運行,這正是我在使用moveToThread()方法和QThread時得到的(正確)行爲。

如何獲得我的QRunnable(下面的示例中的Foo)完全在共用線程中運行? 或者是我做錯了什麼或理解錯誤?

以下POC演示了此問題:

foo.h中

#ifndef FOO_H 
#define FOO_H 

#include <QObject> 
#include <QRunnable> 
#include <QEventLoop> 

class Foo : public QObject, public QRunnable 
{ 
    Q_OBJECT 
public: 
    explicit Foo(int data, QObject *parent = 0); 
    void run(); 
signals: 
    void startWorking(); 
public slots: 
    void doWork(); 

private: 
    QEventLoop eventLoop; 
    int data; 
}; 

#endif // FOO_H 

Foo.cpp中

#include "foo.h" 

#include <QThread> 
#include <QDebug> 

Foo::Foo(int d, QObject *parent) : 
    QObject(parent), eventLoop(this), data(d) 
{ 
} 

void Foo::run() 
{ 
    qDebug() << "run() in: " << QThread::currentThread(); 
    connect(this, SIGNAL(startWorking()), this, SLOT(doWork())); 
    emit startWorking(); 
    eventLoop.exec(); 
} 

void Foo::doWork() 
{ 
    qDebug() << "doWork() in: " << QThread::currentThread(); 
} 

的main.cpp

#include <QCoreApplication> 
#include <QThreadPool> 

#include "foo.h" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    Foo *foo = new Foo(42); 

    QThreadPool::globalInstance()->start(foo); 

    return a.exec(); 
} 

但是,請注意,在我的真實代碼中,信號不會立即發射出去,因爲它會在我收到網絡上的一些數據之後。

PS:POC也可以找到here

回答

1

也許你可以在你的class Foo邏輯一分爲二。然後將所有信號轉發給工作對象。 所以現在你的插槽將在池線程上調用。
但是,QThreadPool專爲執行大量短任務而不會創建太多併發線程而設計。一些任務已經排隊等待其他人完成。如果這不是你的意圖,你可能想回到老的QThread並改用它。

+0

有趣的是,我會給出這個答案。 – Paul

+0

+1,標記爲答案,因爲我解決了問題,不完全如您所說,因爲我在worker中運行事件循環,而不是在runnable中運行。 – Paul

0

由於您的連接調用

connect(this, SIGNAL(startWorking()), this, SLOT(doWork())); 

用於連接類型的默認參數,這將是一個Qt ::自動連接。 該信號是從池池中發出的,並且該池仍然屬於foo,它具有與主線程相關的線程。自動連接將決定將插槽放入主線程的事件隊列中。

有兩種方法可以解決這個問題:

1.

connect(this, SIGNAL(startWorking()), this, SLOT(doWork()), Qt::DirectConnection); 

並取出eventloop.exec();

2.

在運行方法

,連接信號和時隙之前移動Foo對象到當前線程。用QEventLoop
主控器QRunnable和工人QObject,你的工作線程中調用run()方法QEventLoop::exec之前創建:

+0

第二個解決方案將不起作用,因爲我沒有引用池中的線程,我不知道哪個線程將被分配到工作。第一個不會工作,因爲信號不會立即發射出去,並且由於我沒有事件循環,所以線程將在run()返回後退出。 – Paul

+1

您無法將對象移動到當前線程,因爲您只能從所有者線程「推送」。使用Qt :: DirectConnection也是一個壞主意,因爲這並不能保證在工作線程上調用槽(如同op似乎期望的那樣) – spiritwolfform