QT5 QThread
https://blog.csdn.net/zy19940906/article/details/54412600
序
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; };
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的一些用法及生命周期。
如下圖所示
參考資料:傳送門
本次其實還有許多的東西可以說,只是做了個簡單的圖表,慢慢來,現在沒有時間補充,總結完這些,其實理解上的話也比較簡單,暫時就先這樣,當做挖個坑吧= =。
========================= End