2010-07-21 53 views
16

我有一個以下列方式實現的活動對象。它用於在後臺執行長時間的任務。主線程通過向公共時隙發送信號來調用任務(即,doTask)。這是一個精簡的例子(未經測試)。沒有連接的調用槽方法?

class MyTask : public QObject 
{ 
    Q_OBJECT 

public: 
    MyTask(); 
    ~MyTask(); 

public slots: 
    void doTask(int param); 

private slots: 
    void stated(); 

signals: 
    void taskCompleted(int result); 

private: 
    QThread m_thread; 
}; 


MyTask::MyTask() 
{ 
    moveToThread(&m_thread); 
    connect(&m_thread, SIGNAL(started()), this, SLOT(started())); 
    m_thread.start(); 
} 

MyTask::~MyTask() 
{ 
    // Gracefull thread termination (queued in exec loop) 
    if(m_thread.isRunning()) 
    { 
     m_thread.quit(); 
     m_thread.wait(); 
    } 
} 

void MyTask::started() 
{ 
    // initialize live object 
} 

void MyTask::doTask(int param) 
{ 
    sleep(10); 
    emit taskCompleted(param*2); 
} 

只要信號調用doTask(),該(應該)按預期工作。但是,如果主線程直接調用doTask(),它將由主線程執行。對於某些任務,即使插槽方法是直接調用的,我也希望通過活動對象的線程強制執行。

我可以在doTask()前添加代碼來檢查當前線程是否是m_thread,在這種情況下它會執行該方法。如果不是,我希望doTask()向'this'發出一個信號,以便調用doTask()在m_thread exec循環中排隊並儘快執行。

我該怎麼做?

編輯:基於建議的答案,這裏是新的代碼。即使直接由主線程調用,doTask方法現在也會委託活動objet的線程執行。通過信號調用仍然按預期工作。

class MyTask : public QObject 
{ 
    Q_OBJECT 

public: 
    explicit MyTask(QObject *parent = 0); 
    ~MyTask(); 

public slots: 
    void doTask(int param); 

private slots: 
    void doTaskImpl(int param); 

signals: 
    void taskCompleted(int result); 

private: 
    QThread m_thread; 
}; 

MyTask::MyTask(QObject *parent) : QObject(parent) 
{ 
    moveToThread(&m_thread); 
    m_thread.start(); 
} 

MyTask::~MyTask() 
{ 
    // Gracefull thread termination (queued in exec loop) 
    if(m_thread.isRunning()) 
    { 
     m_thread.quit(); 
     m_thread.wait(); 
    } 
} 

void MyTask::doTask(int param) 
{ 
    QMetaObject::invokeMethod(this, "doTaskImpl", Q_ARG(int, param)); 
} 

void MyTask::doTaskImpl(int param) 
{ 
    // Do the live oject's asynchronous task 
    sleep(10); 
    emit taskCompleted(param*2); 
} 

這是我能找到的最簡單的實現來支持在單獨的線程中執行異步方法。只要線程啓動,doTask()方法的調用就會排隊並處理。當從對象線程調用時,它將立即執行(不排隊)。

請注意,只有在啓動線程時纔會發出啓動()信號。這意味着在啓動線程之前排隊的doTask()方法調用將在啓動()方法插槽被調用之前執行。這是我從初始實施中刪除它的原因。因此,對象初始化應該最好在構造函數中執行。

+0

我看到這裏有一個問題正確,則

moveToThread(&m_thread);

將失敗有一位家長。如果MyTask是由父級創建的,您的代碼是否工作? – andref 2010-07-28 14:18:19

+0

一個類似的問題也已經在[QT +如何從不同線程中運行的自定義C++代碼中調用插槽](http:// stackoverflow。COM /問題/ 1144240/QT-如何撥號槽 - 從定製-C代碼運行-IN-A-不同的線程)。 – Trilarion 2014-05-23 09:35:05

回答

19

您想致電QMetaObject::invokeMethod來做到這一點。在你的情況下,它會看起來像

MyTask *task; 
int param; 
// ... 
// Will automatically change threads, if needed, to execute 
// the equivalent of: 
// (void)task->doTask(param); 
QMetaObject::invokeMethod(task, "doTask", Q_ARG(int, param)); 
+0

這真棒。它像一個魅力。 看我編輯使用的代碼。 – chmike 2010-07-22 09:11:08

+4

有沒有可能做到這一點,而不使用字符串名稱的方法? – 2017-01-27 15:59:37

3

關於我想補充的唯一的改進是爲了節省一些時間查找的方法:

class MyTask { 
// ... 
private: 
    int m_doTaskImplIndex; 
}; 

MyTask::MyTask() : 
    //... 
    m_doTaskImplIndex(metaObject()->indexOfMethod("doTaskImpl")) 
    //... 
{} 

void MyTask::doTask(int param) 
{ 
    metaObject()->method(m_doTaskImplIndex).invoke(this, Q_ARG(int, param)); 
} 
+1

通過這項額外的努力,您獲得多少收益? – 2012-05-29 13:48:09

+0

傳遞給'indexOfMethod'的方法的簽名應該包含參數並且處於規範化形式([link](https://qt-project.org/doc/qt-5.0/qtcore/qmetaobject.html#indexOfMethod))正確的代碼應該是'm_doTaskImplIndex(metaObject() - > indexOfMethod(metaObject() - > normalizedSignature(「doTaskImpl(int)」)))'' – 2013-08-17 16:22:49

0

那麼,如何包裝這一切到一個不錯的課程?
我還添加了一個插槽finishPlease,它將作爲消息待辦事項列表中的最後一個元素添加,並在主程序實際處理完所有未決消息並將其終止之前向主程序提供反饋。

class Threaded : public QObject 
{ 
    Q_OBJECT 
public: 
    Threaded() { 
     thread = new QThread(this); 
     this->moveToThread(thread); 
     connect(thread, SIGNAL(started()), this, SLOT(init()), \ 
                Qt::QueuedConnection); 
     thread->start(); 
    } 

    virtual ~Threaded() { 
     thread->exit(); 
     thread->wait(); 
     delete thread; 
    } 

signals: 
    void okayKillMe(); 

public slots: 
    virtual void init() = 0; 
    void finishPlease() {emit okayKillMe();} 

protected: 
    QThread* thread; 
}; 

class MyClass : public Threaded 
{ 
    Q_OBJECT 
public: 
    MyClass() { } 
    virtual ~MyClass() { } 

public slots: 
    void init() { } 
    void doStuff() { } 
    void doOtherStuff(int* data) { } 

}; 
1

我懷疑MyTask中有一個bug。該文件說,你不能移動的物體,如果另一個線程:如果我理解Qt的內部,如果parent不是0