翻譯 | 您沒有作錯(線程)

本文翻譯自: https://woboq.com/blog/qthrea...
原做者: Olivier Goffart
發佈時間:2013年1月22日

  這篇文章是關於QThread的使用的。這是對我當時的同事Brad三年前的博客帖子的回答:"您作錯了"。 html

  Brad在他的博客文章中解釋說,他看到許多用戶經過對QThread進行子類化,在該子類中添加一些槽並在構造函數中執行如下操做來濫用QThread:async

moveToThread(this);

  他們把線程移動到本身類內。正如Brad所提到的,這是錯誤的:QThread應該是管理線程的接口。所以,應該在建立線程中使用它。 函數

  這樣,就沒法在該線程中運行QThread對象中的槽,而且在QThread的子類中具備槽是一種很差的作法。 性能

  可是,Brad繼續並徹底不鼓勵使用QThread的任何子類。他聲稱這違反了正確的面向對象設計。這是我不一樣意的地方。放入代碼run()是擴展QThread的一種有效的面向對象方法:QThread表示一個僅啓動事件循環的線程,子類表示一個被擴展以執行其工做的線程run()this

  Brad上任後,該社區的一些成員就反對對QThread進行子類化進行了討伐。問題在於,有不少徹底合法的緣由能夠繼承QThread。 線程

  在Qt 5.0和Qt 4.8.4中,更改了QThread的文檔,所以示例代碼不涉及子類。查看Qt 4.8 QThread文檔的第一個代碼示例(更新的文檔已經修復)。它具備許多樣板行,僅用於在線程中運行一些代碼。並且甚至存在泄漏:QThread永遠不會退出並被銷燬。 翻譯

  我在IRC上被問到一個用戶的問題,該用戶遵循該示例,以便在線程中運行一些簡單的代碼。他很難弄清楚如何正確銷燬線程。這就是促使我撰寫此博客條目的緣由。 設計

  若是容許子類化QThread,那麼您將得到:code

class WorkerThread : public QThread {
    void run() {
        // ...
    }
};

void MyObject::startWorkInAThread()
{
    WorkerThread *workerThread = new WorkerThread;
    connect(workerThread, SIGNAL(finished()),
            workerThread, SLOT(deleteLater()));
    workerThread->start();
}

  此代碼再也不泄漏,而且更加簡單,而且不會建立無用的對象,所以開銷較小。 server

  Qt線程示例threadedfortuneserver是使用此模式運行阻塞操做的示例,而且比使用worker對象的等效示例要簡單得多。

  我已經向文檔提交了補丁, 以避免再次阻止對QThread的子類化。

經驗法則


何時子類化,何時不子類化?

  • 若是您確實不須要線程中的事件循環,則應該子類化
  • 若是須要事件循環並處理線程中的信號和槽,則可能不須要子類化

改用QtConcurrent呢?

  QThread的級別很低,您最好使用更高級別的API,例如QtConcurrent。

  如今,QtConcurrent有其自身的一系列問題:它與單個線程池綁定,所以若是要運行阻塞操做,它不是一個好的解決方案。在其實現中還存在一些問題,這些問題會帶來一些性能開銷。全部這些都是能夠修復的。也許甚至Qt 5.1也會有所改進。

  一個很好的選擇也是C ++ 11與標準庫 std::thread 和std::async它們如今在一個線程中運行的代碼的標準方式。好消息是它仍然能夠在Qt上正常工做:全部其餘Qt線程原語均可以與本機線程一塊兒使用。(若是須要,Qt將自動建立一個QThread來建立)

關於更多

  • 請查看往期文章「你這樣作是錯的…(翻譯文)」
相關文章
相關標籤/搜索