1. 程式人生 > >Qt使用多執行緒的一些心得——2.繼承QObject的多執行緒使用方法

Qt使用多執行緒的一些心得——2.繼承QObject的多執行緒使用方法

現在Qt官方並不是很推薦繼承QThread來實現多執行緒方法,而是極力推崇繼承QObject的方法來實現,當然用哪個方法實現要視情況而定,別弄錯了就行,估計Qt如此推崇繼承QObject的方法可能是QThread太容易用錯的原因。

1. 前言

上一篇介紹了傳統的多執行緒使用方法——繼承QThread來實現多執行緒,這也是很多框架的做法(MFC),但Qt還有一種多執行緒的實現方法,比直接繼承QThread更為靈活,就是直接繼承QObject實現多執行緒。

QObject是Qt框架的基本類,但凡涉及到訊號槽有關的類都是繼承於QObjectQObject是一個功能異常強大的類,它提供了Qt關鍵技術訊號和槽的支援以及事件系統的支援,同時它提供了執行緒操作的介面,也就是QObject

是可以選擇不同的執行緒裡執行的。

QObject的執行緒轉移函式是:void moveToThread(QThread * targetThread) ,通過此函式可以把一個頂層Object(就是沒有父級)轉移到一個新的執行緒裡。

QThread非常容易被新手誤用,主要是QThread自身並不生存在它run函式所在的執行緒,而是生存在舊的執行緒中,此問題在上一篇重點描述了。由於QThread的這個特性,導致在呼叫QThread的非run函式容易在舊執行緒中執行,因此人們發現了一個新的魔改QThread的方法: 
人們發現,咦,QThread也繼承QObjectQObject

有個函式void moveToThread(QThread * targetThread)可以把Object的執行執行緒轉移,那麼:(下面是非常不推薦的魔改做法,別用此方法):

  1. classMyThread:publicQThread{
  2. public:
  3. MyThread()
  4. {
  5. moveToThread(this);
  6. }
  7. ……
  8. };

直接把MyThread整個轉移到MyThread的新執行緒中,MyThread不僅run,其它函式也在新執行緒裡了。這樣的確可以執行正常,但這並不是QThread設計的初衷,Qt還專門發過一篇文章來吐槽這個做法。

在Qt4.8之後,Qt多執行緒的寫法最好還是通過QObject

來實現,和執行緒的互動通過訊號和槽(實際上其實是通過事件)聯絡。

2.繼承QObject的多執行緒實現

QObject來實現多執行緒有個非常好的優點,就是預設就支援事件迴圈(Qt的許多非GUI類也需要事件迴圈支援,如QTimerQTcpSocket),QThread要支援事件迴圈需要在QThread::run()中呼叫QThread::exec()來提供對訊息迴圈的支援,否則那些需要事件迴圈支援的類都不能正常傳送訊號,因此如果要使用訊號和槽,那就直接使用QObject來實現多執行緒。

2.1 建立及銷燬執行緒

繼承QObject多執行緒的方法執行緒的建立很簡單,只要讓QThreadstart函式執行起來就行,但是需要注意銷燬執行緒的方法 
線上程建立之後,這個QObject的銷燬不應該在主執行緒裡進行,而是通過deleteLater槽進行安全的銷燬,因此,繼承QObject多執行緒的方法在建立時有幾個槽函式需要特別關注:

  • 一個是QThreadfinished訊號對接QObjectdeleteLater使得執行緒結束後,繼承QObject的那個多執行緒類會自己銷燬
  • 另一個是QThreadfinished訊號對接QThread自己的deleteLater,這個不是必須,下面官方例子就沒這樣做

看看Qt官方文件的例子:

  1. classWorker:publicQObject
  2. {
  3. Q_OBJECT
  4. public slots:
  5. void doWork(constQString&parameter){
  6. QString result;
  7. /* ... here is the expensive or blocking operation ... */
  8. emit resultReady(result);
  9. }
  10. signals:
  11. void resultReady(constQString&result);
  12. };
  13. classController:publicQObject
  14. {
  15. Q_OBJECT
  16. QThread workerThread;
  17. public:
  18. Controller(){
  19. Worker*worker =newWorker;
  20. worker->moveToThread(&workerThread);
  21. connect(&workerThread,&QThread::finished, worker,&QObject::deleteLater);
  22. connect(this,&Controller::operate, worker,&Worker::doWork);
  23. connect(worker,&Worker::resultReady,this,&Controller::handleResults);
  24. workerThread.start();
  25. }
  26. ~Controller(){
  27. workerThread.quit();
  28. workerThread.wait();
  29. }
  30. public slots:
  31. void handleResults(constQString&);
  32. signals:
  33. void operate(constQString&);
  34. };

使用QObject建立多執行緒的方法如下:

  • 寫一個繼承QObject的類,對需要進行復雜耗時邏輯的入口函式宣告為槽函式
  • 此類在舊執行緒new出來,不能給它設定任何父物件
  • 同時宣告一個QThread物件,在官方例子裡,QThread並沒有new出來,這樣在析構時就需要呼叫QThread::wait(),如果是堆分配的話, 可以通過deleteLater來讓執行緒自殺
  • 把obj通過moveToThread方法轉移到新執行緒中,此時object已經是線上程中了
  • 把執行緒的finished訊號和object的deleteLater槽連線,這個訊號槽必須連線,否則會記憶體洩漏
  • 正常連線其他訊號和槽(在連線訊號槽之前呼叫moveToThread,不需要處理connect的第五個引數,否則就顯示宣告用Qt::QueuedConnection來連線
  • 初始化完後呼叫'QThread::start()'來啟動執行緒
  • 在邏輯結束後,呼叫QThread::quit退出執行緒的事件迴圈

使用QObject來實現多執行緒比用繼承QThread的方法更加靈活,整個類都是在新的執行緒中,通過訊號槽和主執行緒傳遞資料,前篇文章的例子用繼承QObject的方法實現的話,程式碼如下: 
標頭檔案(ThreadObject.h):

  1. #include<QObject>
  2. #include<QMutex>
  3. classThreadObject:publicQObject
  4. {
  5. Q_OBJECT
  6. public:
  7. ThreadObject(QObject* parent = NULL);
  8. ~ThreadObject();
  9. void setRunCount(int count);
  10. void stop();
  11. signals:
  12. void message(constQString& info);
  13. void progress(int present);
  14. public slots:
  15. void runSomeBigWork1();
  16. void runSomeBigWork2();
  17. private:
  18. int m_runCount;
  19. int m_runCount2;
  20. bool m_isStop;
  21. QMutex m_stopMutex;
  22. };

cpp檔案(ThreadObject.cpp):

  1. #include"ThreadObject.h"
  2. #include<QThread>
  3. #include<QDebug>
  4. #include<QMutexLocker>
  5. #include<QElapsedTimer>
  6. #include<limits>
  7. 相關推薦

    Qt使用執行一些心得——2.繼承QObject執行使用方法

    現在Qt官方並不是很推薦繼承QThread來實現多執行緒方法,而是極力推崇繼承QObject的方法來實現,當然用哪個方法實現要視情況而定,別弄錯了就行,估計Qt如此推崇繼承QObject的方法可能是QThread太容易用錯的原因。 1. 前言 上一篇介

    Qt使用執行一些心得——1.繼承QThread的執行使用方法

    1.摘要 Qt有兩種多執行緒的方法,其中一種是繼承QThread的run函式,另外一種是把一個繼承於QObject的類轉移到一個Thread裡。 Qt4.8之前都是使用繼承QThread的run這種方法,但是Qt4.8之後,Qt官方建議使用第二種方法。

    Java面試專題-執行篇(2)- 鎖和執行

    ​   開篇介紹 大家好,公眾號【Java極客思維】近期會整理一些Java高頻面試題分享給小夥伴,也希望看到的小夥伴在找工作過程中能夠用得到!本章節主要針對Java一些多執行緒高頻面試題進行分享。   Q1: 樂觀鎖 和 悲觀鎖 樂觀鎖: 樂觀鎖(Optimistic Locking)

    關於使用繼承QObject實現執行的理解——Qt推薦的方法

    概念 多執行緒的使用主要是為了處理比較耗時的過程。這可以用以下圖來形象地描述: 目前,由於繼承QObject的多執行緒實現方法更加靈活,Qt官方推薦使用該方法實現多執行緒。 想用圖來描述實現的過程,發現也不好表達,將就著看吧: 步驟 1、創鍵一個繼承於 QObje

    python執行———2、建立執行的兩種方式

     法一、使用Thread類例項化 法二、繼承Thread來實現多執行緒 #對於io操作來說,多執行緒和多程序效能差別不大 #1、使用Thread類例項化 import time import threading def get_detail_html(url): prin

    Java執行學習心得

    執行緒的概念: 大白話的意思就是,執行緒存在於程序中,它主要是為了提程序中CPU(程序有記憶體,執行緒沒有)的利用率,即提高效率。 主執行緒:執行main方法的執行緒,是它產生其他執行緒,通常它是最後結束,因為它要執行關閉其他執行緒的工作,它不會直接關閉其他執行緒,而只是傳送指令過去。

    java執行入門案例(2)之執行簡單應用

      上一篇文章:java多執行緒案例(1)之簡單銀行取款問題及其優化 我大概介紹了一下Java程式碼優化的問題,主要針對出學者而言,這一次我要介紹多執行緒應用的簡單案例 。網上有許多多執行緒的案例,但大多都挺複雜的,今天我主要目的也是介紹一下多執行緒應用的簡單案例,讓初學

    python(2.7)中執行使用舉例

    python(2.7)中多執行緒使用舉例 python27中多執行緒使用舉例 下邊的程式碼都不難理解,不做多餘解釋。唯一有困惑的地方已經在原始碼中註釋說明。這裡也不做多執行緒編碼知識的講解。把這幾種形式(主要是第三種)練成muscle mem

    Java執行程式設計筆記2:synchronized同步方法

    非執行緒安全會在多個執行緒對同一個物件中的例項變數進行併發訪問時發生,產生的結果就是髒讀,也就是取到的資料是被更改過的。執行緒安全就是獲得的例項變數的值是經過同步處理的。 方法內的變數是執行緒安全的 方法內的變數是執行緒安全的。非執行緒安全的問題存在於例項變數中,如果是方法內部的私有變數,不存在非執行緒安

    Java面向物件與執行綜合實驗(一)之封裝、繼承

    編寫一個程式,實現檔案管理系統中的使用者管理模組。要求模組中實現使用者的模擬登入過程。通過使用者輸入,獲取使用者名稱和口令;與事先記錄在程式中的使用者資訊進行對比,通過口令驗證後才能使用系統。使用者分為系統管理人員、檔案錄入人員,檔案瀏覽人員三類,相關類圖如下所示。 (1)要求在使用者類中

    Java 執行高併發 2 — CAS 無鎖

    在 Java 併發程式設計裡面,最可愛的就是無鎖了,非常巧妙,精彩絕倫 額。O__O "… 那麼什麼是無鎖? 顧名思義,在併發情況下采用無鎖的方式實現物件操作的原子性,保證資料一致性、安全性、正確性

    Java執行學習筆記2

    本文是我學習Java多執行緒以及高併發知識的第一本書的學習筆記, 書名是<<Java多執行緒程式設計核心技術>>,作者是大佬企業高階專案經理 高洪巖前輩,在此向他致敬。我將配合開發文件以及本書和其他的部落格 奉獻著的文章來學習,同時做一些簡單的總結。有

    Java執行一些基礎知識

    最近複習了一些多執行緒方面的基礎知識,做一下總結,多以自己的理解來文字敘述,如果有漏點或者理解錯的地方,歡迎各位大佬多多指出; ps:執行緒分為使用者執行緒和守護執行緒,當程式中的所有的使用者執行緒都執行完了之後,JVM就退出執行了,下面所講的都是使用者執行緒為例,我們一般建立一個新執行緒物件,預設都是使用者

    Java 執行下,2種安全、效能靠譜的單例模式

    懶漢式-雙重核驗: package com.zzf.concurrence.singleinstance; /** * 懶漢式-雙重核驗 * @author zzf * */ public class SingleEHan { private Single

    c++單例模式[2]--Meyers方式--執行單例

    [1]單例模式中最大的缺陷就是執行緒安全與判斷的開銷 #pragma once #include <iostream> #include <thread> using names

    Python學習【第24篇】:死鎖,遞迴鎖,訊號量,Event事件,執行Queue python併發程式設計之執行2------------死鎖與遞迴鎖,訊號量等

    python併發程式設計之多執行緒2------------死鎖與遞迴鎖,訊號量等 一、死鎖現象與遞迴鎖 程序也是有死鎖的 所謂死鎖: 是指兩個或兩個以上

    C++ 執行框架 (2):Mutex 互斥和 Sem 訊號量

    互斥和訊號量是多執行緒程式設計的兩個基礎,其原理就不詳細說了,大家去看看作業系統的書或者網上查查吧。 對於互斥的實現,無論什麼作業系統都離不開三個步驟 1.初始化互斥鎖 2.鎖操作 3.解鎖操作 對於不同的系統只是實現的函式有一些不同而已,但是功能其實都大同小異,在

    Qt執行 - 繼承QObject方式

    Qt執行緒 - 繼承QObject方式 Qt使用執行緒有兩種方式,在新版本的Qt中,Qt官方推薦使用繼承QObject的方式,本文件記錄使用此方法執行緒的實驗過程。 本文轉自:http://beself.top/2018/11/09/qt-thread-inhe

    Unity應用架構設計(10)——繞不開的協程和執行(Part 2

    在上一回合談到,客戶端應用程式的所有操作都在主執行緒上進行,所以一些比較耗時的操作可以在非同步執行緒上去進行,充分利用CPU的效能來達到程式的最佳效能。對於Unity而言,又提供了另外一種『非同步』的概念,就是協程(Coroutine),通過反編譯,它本質上還是在主執行緒上的優化手段,並不屬於真正的多執行緒

    面試知識點—執行同步【2】之CyclicBarrier

    繼續總結多執行緒同步常用的方法或者類,上一節介紹了CountDownLatch,這次介紹一下它的加強版本CyclicBarriar。 CyclicBarriar–迴圈柵欄 CyclicBarriar的一個特別典型的應用場景是:有一個比較大型的任務,需要分配好