Qt多執行緒基礎(一)執行緒同步之互斥鎖同步

2021-08-20 14:15:41 字數 3844 閱讀 5191

一、直接使用qmutex進行同步

建立執行緒方法:繼承自qthread,重寫void run()函式,呼叫成員start()啟動執行緒,start()中可加入優先順序引數。

互斥鎖同步方法:void run()函式中使用qmutex來實現同步,當多個執行緒訪問共享變數時,應使用lock/trylock和unlock將對共享變數的操作**包裹,以保證同步訪問共享變數。(c++中引起執行緒安全的共享資源只有兩種:全域性變數和靜態變數)

示例**中兩個thread均繼承自qthread(),為了保證互斥鎖對兩個執行緒均可見,qmutex在乙個執行緒cpp檔案中定義,另乙個執行緒檔案做extern宣告。

示例**如下:

thread.h

[cpp] 

view plain

copy

#ifndef mythread_h

#define mythread_h

#include 

#include 

class

mythread:

public

qthread  

;  #endif // mythread_h

thread.cpp

[cpp] 

view plain

copy

#include "mythread.h"

#include 

inti=50;  

qmutex mutex;  

mythread::mythread(qstring name):qthread(),mname(name)  

void

mythread::run()  

*/i++;  

i*=2;  

qdebug()<

->mname<

mutex.unlock();  

qdebug()<

->mname<<

"stop running.."

<

sleep(1);  

}  thread2.h

[cpp] 

view plain

copy

#ifndef mythread2_h

#define mythread2_h

#include 

#include 

class

mythread2:

public

qthread  

;  #endif // mythread2_h

thread2.cpp

[cpp] 

view plain

copy

#include "mythread2.h"

#include 

extern

inti;  

extern

qmutex mutex;  

mythread2::mythread2(qstring name):qthread(),mname(name)  

void

mythread2::run()  

*/i--;  

i/=2;  

qdebug()<

->mname<

mutex.unlock();  

qdebug()<

->mname<<

"stop runnning.."

<

sleep(1);  

}  main.cpp

[cpp] 

view plain

copy

#include "mythread.h"

#include "mythread2.h"

intmain(

intargc, 

char

*argv)  

實驗結果:

二、使用互斥鎖類qmutexlocker(**raii)

問題:使用qmutex的上鎖、解鎖操作直接同步會有乙個致命缺陷:當**提前退出時(如函式中多處return或c++丟擲異常),可能並未執行unlock(),若其他執行緒採用lock()阻塞式上鎖會一直被阻塞等待釋放,導致資源洩露。

解決:根據raii的思想,我們應該盡量使用物件管理資源,構造時獲取互斥鎖,析構時釋放鎖。(參見effective c++條款13)

具體來講,qmutexlocker作為乙個便利類,可以解決以下兩種函式有多個出口的情況:

(1)第一種情況是函式內部多次return,如果直接使用qmutex上鎖,必須保證每個return之前都及時釋放鎖資源(每個return前都要加上unlock()),否則當前執行緒的run()退出時另乙個執行緒的run()無法獲取鎖,造成死鎖。如下例所示:

[cpp] 

view plain

copy

intcomplexfunction(

intflag)  

break

;  default

:  if

(flag > 10)  

return

-1;  

break

;  }  

return

retval;  

}  如果我們按上述**所示,使用qmutexlocker管理qmutex,由於函式中的qmutexlocker是乙個區域性物件,因此return的時候一定會呼叫析構並在析構內部完成互斥鎖的釋放。

(2)另一種情況是c++丟擲異常的情況:c++標準裡明確規定丟擲異常時仍能保證區域性物件的析構呼叫,這也是raii技術的保證。也就是說由於qmutexlocker是區域性物件,所以一旦遇到函式退出時,區域性物件被釋放都會呼叫析構,析構內部會釋放鎖。(參見effective c++條款29)

至於為何c++丟擲異常時仍能保證釋放區域性物件(棧上變數),這是c++標準規定,請參看:

因此,qt提供了互斥鎖類qmutexlocker,當qmutexlocker作為區域性物件時,函式中途return或丟擲異常時均會呼叫析構釋放物件,而該類的析構函式內部呼叫了引數繫結的qmutex對應的unlock()函式,這也是raii技術的基礎保證。空口無憑,如圖為證,這是qmutexlocker內部的析構函式實現:

可以看到,析構裡面呼叫unlock()函式,而unlock()函式內部呼叫mutex()->unlock(),mutex()是乙個常量函式,返回qmutexlocker繫結的qmutex,因此mutex()->unlock()呼叫了qmutex的unlock()函式,完成了對互斥鎖的解鎖。如圖:

可以看到上方的qmutex類中將qmutexlocker宣告成了qmutex的友元!因此qmutexlocker可以在析構中呼叫qmutex的unlock()完成鎖資源的釋放。這就使得當run()函式有多個出口退出時(多處return或丟擲異常),析構被呼叫並及時完成互斥鎖的釋放,從而避免鎖資源的洩露問題。

另外乙個用到raii思想的技術比如c++stl的智慧型指標,也是為了避免堆上空間未及時釋放的情況。如果使用普通指標申請堆空間,函式中途丟擲異常(比如另乙個指標申請空間失敗,丟擲bad_alloc異常),那該指標申請的空間將無法釋放,有人說使用捕獲異常在catch中釋放所有資源,比如此處洩露的記憶體,但這並不是個好辦法,於是根據raii思想,智慧型指標產生了,當智慧型指標的引用計數減為0時會釋放這塊記憶體(delete)。

看到這裡,終於放心了嗎?這是為什麼qt也推薦使用qmutexlocker的原因:raii技術可以讓我們寫出異常安全的**。

Qt 多執行緒同步之互斥鎖

qmutex需要配對使用lock 和unlock 來實現 段的保護 qmutexlocker是另外乙個簡化了互斥量處理的類。qmutexlocker建構函式接受乙個互斥量作為引數並將其鎖定,qmutexlocker的析構函式則將此互斥量解鎖,所以在qmutexlocker例項變數的生存期內的 段得到...

QT 多執行緒互斥

今天是七夕佳節,我又來啦,最近到處跑累死人。無論任何程式語言,說到多執行緒,我們肯定要非常注意臨界資源的訪問問題。個人認為多執行緒互斥的內容是比較多,而且比較複雜的,這次的坑的篇幅可能比較長,盡可能用最通俗的語言來表達 什麼是臨界資源?當多個執行緒需要某個資源,而這個資源只能在同一時刻被乙個執行緒所...

Qt多執行緒互斥

目錄 一 多執行緒與臨界資源的依賴 現象分析 二 互斥和解決方法 三 qmutex的主要成員函式和使用 四 示例 五 小結 除了上一節所說的,多執行緒在 執行的時序上會有依賴,那麼其他地方是否還有所依賴呢?答案是有的,也就是與臨界資源的問題,所謂臨界資源是指每次只允許乙個執行緒進行訪問 讀或寫 的資...