QT5 QThread


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM