1. 程式人生 > >效能特性測試系列4——QT執行緒與std::thread(下)之QThread

效能特性測試系列4——QT執行緒與std::thread(下)之QThread

 其實用法早就總結了,但是因為自身事務原因,一直沒有比較他們間的效率,甚至各種互斥量,條件變數之間的效率,更多是集中看了下他們各自的api和特點,所以估計以後還需要補充或者是另開一篇來講= =。

QThread的執行緒用法上與std::thread相比有較大的區別,4.4版本之前是繼承的方式來使用執行緒(個人猜測可能是因為那會兒c++11還沒出來,std::function和std::bind沒有,所以繼承是實現訊息回撥比較方便的方式,當然僅僅是猜測,有興趣可以查證),但4.4之後開始,官方建議不要再用繼承的方式來使用執行緒,而是通過訊號槽的方式來取代。

測試環境:Qt5.7,vs2015。

一、QThread:

 如下圖所示:
這裡寫圖片描述
看不清楚圖可以直接在官網看:傳送門
 1、繼承自QObject(截圖沒截上)

 2、啟動執行緒:start()函式,啟動後呼叫run()函式,run()執行完之後退出執行緒。

 3、wait:有點類似std::thread 的join,但是需要指定時間,並且不是執行緒run函式結束後自動返回,如果不指定,預設會一直等待。所以我一般在用的過程中,需要退出的時候(或者呼叫quit),再wait。

二、兩種用法:

既然QThread有兩種用法,那麼就簡單介紹下吧:
圖裡已經說得很清楚了,那我就不在多說:
這裡寫圖片描述
標頭檔案:

class QtThreadFuncByThread : public
QThread { public: void SetLoop(int loop); int GetSum(); protected: virtual void run(); private: int _Loop = 0; int _Sum = 0; };

cpp

void QtThreadFuncByThread::SetLoop(int loop)
{
    this->_Loop = loop;
}

int QtThreadFuncByThread::GetSum()
{
    return this->_Sum;
}

void
QtThreadFuncByThread::run() { for(int i = 0; i < this->_Loop; ++i) { this->_Sum++; } int id =(int)QThread::currentThreadId(); emit Log::GetInstance()->LogStr(QString("qt繼承方式子執行緒id: %1;"). arg(id)); }

使用:


    QtThreadFuncByThread thread;
    thread.SetLoop(loop);
    thread.start();
    thread.wait(100);

2、訊號槽方式(推薦用法):
至於為啥推薦呢,直接給個傳送門吧:傳送門
這裡寫圖片描述
至於程式碼,官網給了例子:傳送門
我再寫個簡化版的:

    QThread * thread = new QThread;
    QtThreadFuncClass* funcclass = new QtThreadFuncClass;
    funcclass->SetLoop(loop);
    funcclass->moveToThread(thread);
    QObject::connect(thread, &QThread::started, funcclass, &QtThreadFuncClass::ThreadFunc, Qt::DirectConnection);
    thread->start();
    thread->wait(100);

 簡而言之,你需要做的是把QObject物件,movetothread去,否則你呼叫的訊號槽仍然是在主執行緒。

二、互斥量,鎖,條件變數,原子操作及其他:

 其實std::thread有的那些互斥量,自解鎖,條件變數,future,原子操作等,Qt裡面也能找到對應的類,只是用法,和一些細節性的功能不太一致,其他大致都是相似的,所以我就簡要的整理和彙總下:

 1、互斥量與自解鎖:
如下圖所示:
這裡寫圖片描述

 qt的互斥量只有簡單的QMutex,當然,某種程度上是std::thread裡那幾種結合體,自解鎖也只有一種,如果不記得std::thread有哪些,請看我上一篇。用法比較簡單,我就不貼程式碼了。

 2、讀寫鎖與其自解鎖:
這裡寫圖片描述

 所謂讀寫鎖,顧名思義,即讀鎖定狀態與寫鎖定狀態是不一樣的。例如指定某段讀取區域為lockForRead(),則表示這段程式碼僅僅是對資源進行讀取,沒有改變,所有執行緒可以共享該資源,無需阻塞;另一段改寫某資源區域為LockForWrite(),則表示這段程式碼需要改寫資源,其他執行緒需要阻塞,同時,lockForRead()鎖定的讀取資原始碼也會被鎖住,別的執行緒無論是讀還是寫會阻塞。

 3、訊號量:
這裡寫圖片描述

 圖中其實把函式介紹的比較清楚了,即先指定一定數量的訊號量,在實際用的過程中可用於一個類似生產者消費者模式,一個執行緒負責生產,如果訊號量沒有空位置了,rlease(加一個位置),另一個執行緒負責消費,如果有空位置,就消費一個(acquire)。

 4、條件變數:
這裡寫圖片描述

 前面兩個讀寫鎖和訊號量這些還和std::thread提供的機制有區別(因為沒有,當然,想實現也可以用互斥量這些自己寫一個)。條件變數幾乎是沒啥差異,用法也都差不多,函式已經在圖中列出來了。

 5、原子操作:
這裡寫圖片描述
官網傳送門:傳送門
 一共就如圖所示只有三種,int,integer,pointer,用法也沒太大的區別,不記得在上一篇有沒有說過,原子物件本身也是帶鎖的,多執行緒訪問的時候不用擔心上鎖問題,是為細粒度鎖(mutex這些是粗粒度鎖)。

 6、future:
這裡寫圖片描述
qfuture依賴於Qt Concurrent,下圖接上圖:
這裡寫圖片描述
官網傳送門:傳送門
簡單的說就是一個非同步方法,類似std::async,只執行一次的非同步方法,具體概念用法圖中已經解釋了。
下圖解釋下QFuture和QFutureWatcher關係(官網例子):
這裡寫圖片描述

 7、QThreadPool:
其實這個不算執行緒機制,不應該放上來,隨便看看:
這裡寫圖片描述

三、一張圖:

該圖能較好說明QThread的一些用法及生命週期。
 如下圖所示
這裡寫圖片描述
參考資料:傳送門

 本次其實還有許多的東西可以說,只是做了個簡單的圖表,慢慢來,現在沒有時間補充,總結完這些,其實理解上的話也比較簡單,暫時就先這樣,當做挖個坑吧= =。

ps:基本上,執行緒的一些特性就先這樣,以後有時間再說,下期是網路程式設計,Qt的tcp和udp,以及asio的tcp,udp,時間未知。