C 十分好用的Qt的多執行緒

2021-10-09 11:39:03 字數 4719 閱讀 3500

1、先看一些執行緒相關的類

qthread 

提供了開始乙個新執行緒的方法

qthreadstorage 

提供逐執行緒資料儲存

qmutex 

互斥鎖qmutexlocker 

自動鎖(在建構函式中自動對mutex進行加鎖,在析構函式中進行解鎖。需要用到mutable在const函式中修改被定義的非const變數)

qreadwriterlock 

讀寫鎖qreadlocker&qwritelocker

自動對讀寫鎖加鎖解鎖

qsemaphore 

訊號量qwaitcondition 

條件變數

2、建立乙個簡答的執行緒,run()必須被重寫,因為它是純虛函式。

class mythread : public qthread ;

void mythread::run() ;

跟c++不同的是,qt的exec()必須在主線程,即main()函式中,因為gui執行緒預設是主線程。

跟c++類似的是,用三把鎖和條件變數保護共享資料訪問。

2.1 讀寫鎖 qreadwriterlock 

qreadwritelock lock; 

void readerthread::run()

void writerthread::run()

2.2 訊號量qsemaphore ,相比於互斥鎖,訊號量能保護一定數量的相同資源。

下面用qsemaphore 控制線程對環形緩衝區資源的訪問

const int datasize = 100000;

const int buffersize = 8192;

char buffer[buffersize];

qsemaphore freebytes(buffersize);

qsemaphore usedbytes;

class producer : public qthread

;void producer::run()

}class consumer : public qthread

;void consumer::run()

fprintf(stderr, "\n");

}int main(int argc, char *ar**)

2.3 qwaitcondition 類似於c++中的條件變數,例項化之後,利用wait()阻塞,weakone / weakall類似於c++的notify_one/all。

const int datasize = 100000;

const int buffersize = 8192;

char buffer[buffersize];

qwaitcondition buffernotempty;

qwaitcondition buffernotfull;

qmutex mutex;

int numusedbytes = 0;

class producer : public qthread

;void producer::run()

}class consumer : public qthread

;void consumer::run()

fprintf(stderr, "\n");

}int main(int argc, char *ar**)

3、可重入和執行緒安全

「可重入」與「執行緒安全」被用來說明乙個函式如何用於多執行緒程式。假如乙個類的任何函式在此類的多個不同的例項上,可以被多個執行緒同時呼叫,那麼這個類被稱為是「可重入」的。假如不同的執行緒作用在同乙個例項上仍可以正常工作,那麼稱之為「執行緒安全」的。

大多數c++類天生就是可重入的,因為它們典型地僅僅引用成員資料。任何執行緒可以在類的乙個例項上呼叫這樣的成員函式,只要沒有別的執行緒在同乙個例項上呼叫這個成員函式。舉例來講,下面的temp類是可重入的。

class temp

void increment()

void decrement()

int value() const

private:

int n;

};

但是上述類並不能保證執行緒的安全,因為自增自減並不是原子(atomic)操作,那麼就有可能被擴充套件成以下原子指令:

變數進入暫存器》增加/減少暫存器的值 》再把暫存器額值寫回記憶體之中。

問題會發生在兩個寫者執行緒都使用了舊值,然後在暫存器中增值,寫操作重疊,導致變數只增加了一次。所以,對資源的訪問應該序列化,執行上面三個步驟的時候不應該中斷。使用互斥鎖或者自動鎖是為了讓執行緒安全。

class counter

void increment()

void decrement()

int value() const

private:

mutable qmutex mutex;

int n;

};

4、多執行緒和隱含共享

qt許多值型別使用了所謂的隱含共享(implicit sharing)來優化效能。原理比較簡單,共享類包含乙個指向共享資料塊的指標,這個資料塊中包含了真正原資料與乙個引用計數。把深拷貝轉化為乙個淺拷貝,從而提高了效能。如果深入觀察,假如物件需要對資料進行修改,而引用計數大於1,那麼它應該先detach()。以使得它修改不會對別的共享者產生影響,既然修改後的資料與原來的那份資料不同了,因此不可能再共享了,於是它先執行深拷貝,把資料取回來,再在這份資料上進行修改。例如:

void qpen::setstyle(qt::penstyle style)

void qpen::detach()

翻譯qt手冊原文就是:「隱含共享類可以安全地跨執行緒拷貝,如同別的值型別一樣。它們是完全可重入的。隱含共享真的是"implicit"。它使用組合語言實現了原子性引用計數操作,比用mutex效率高。假如你在多個執行緒中同進訪問相同物件,你也需要用mutex來序列化訪問順序,就如同其他可重入物件那樣。總的來講,隱含共享真的給」隱含「掉了,在多執行緒程式中,你可以把它們看成是一般的,非共享的,可重入的型別,這種做法是安全的。」

5、qt下的p-c模型:

注意:1)生產者首先必須檢查緩衝是否已滿(numusedbytes==buffersize),如果是,執行緒停下來等待buffernotfull條件。如果不是,在緩衝中生產資料,增加numusedbytes,啟用條件 buffernotempty。

2)使用mutex來保護對numusedbytes的訪問。

而且,從鎖定狀態到等待狀態的轉換是原子操作,這阻止了競爭條件的產生。當程式開始執行時,只有生產者可以工作。消費者被阻塞等待buffernotempty條件,一旦生產者在緩衝中放入乙個位元組,buffernotempty條件被激發,消費者執行緒於是被喚醒。

const int datasize = 100000;

const int buffersize = 8192;

char buffer[buffersize];

qsemaphore freebytes(buffersize); //producer執行緒在此區域寫入資料,初始資源數量為buffersize

qsemaphore usedbytes; //consumer執行緒讀取此區域的資料,初始資源數量為0

class producer : public qthread

;//生產者每acquire一次就,使用掉buffer個資源中的乙個,而寫入的字元存入到buffer陣列中

//從而消費者可用讀取字元,從而消費者獲取乙個資源

void producer::run()

}class consumer : public qthread

;void consumer::run()

fprintf(stderr, "\n");

}int main(int argc, char* ar**)

再比如下面這個例子:

4 // 全域性條件變數

5 qwaitcondition mycond;

6 7 // worker類實現

8 class worker : public qpushbutton, public qthread

9

24 25 public slots:

26 void slotclicked()

27

31 32 protected:

33 void run()

34

54 }

55 };

56 57 // 主線程——所有的gui事件都由這個執行緒處理。

58 int main( int argc, char **ar** )

59

70

好用的臨時郵箱十分鐘郵箱推薦(親測)

為什麼要使用臨時郵箱?由於某些地方在註冊帳戶時需要驗證您的電子郵件,但是在您使用經常使用的電子郵件位址註冊後,幾天之內就會出現大量垃圾郵件。有時,您可能需要註冊一些 或其他內容。並且此行為只是偶然的或暫時的。也許您以後將不再使用它,但是註冊時通常需要填寫電子郵件位址,並且您不想使用真實的電子郵件位址...

QT多執行緒的學習

qt通過三種形式提供了對執行緒的支援。它們分別是,一 平台無關的執行緒類,二 執行緒安全的事件投遞,三 跨執行緒的訊號 槽連線。這使得開發輕巧的多執行緒qt程式更為容易,並能充分利用多處理器機器的優勢。多執行緒程式設計也是乙個有用的模式,它用於解決執行較長時間的操作而不至於使用者介面失去響應。在qt...

QT下的多執行緒

一.多執行緒的使用 繼承qthread類,重寫protected void run 函式,則此run函式即為多執行緒將執行的函式,ui類組合此類,呼叫start 函式即開啟此執行緒,並執行run函式 h class test1 public qthread cpp void test1 run vo...