Qt之執行緒同步

2021-07-29 18:14:45 字數 4006 閱讀 2476

qt中實現了如下類,提供執行緒同步機制

qmutex:互斥量。用來確保同一時刻,只能有乙個執行緒訪問某一資源。

qreadwritelock:讀寫鎖。允許同一時刻多個執行緒讀取某一資源,但只要有乙個執行緒在寫該資源,則不允許其他程同時讀取該資源。

qwaitcondition:等待條件。提供了乙個條件變數同步執行緒,當某個條件滿足時,可以傳送訊號通知其他執行緒該條件已經達到。

qsemphone:訊號量。能夠保證同一時刻乙個或多個資源不被併發訪問,經常用在有限的相同資源條件下的執行緒同步。

qt為我們提供了一系列的容器類,比如qlist,qqueue,qstack等,但都是可重入(reentrant)而不是執行緒安全的(thread-safe)。因此當遇到多執行緒的容器共享時,我們就需要實現自己的執行緒安全版本容器,以qqueue為例:

qsafequeue.h

#include #include templateclass qsafequeue

;

qsafequeue.cpp
templateqsafequeue::qsafequeue()

: m_olist(),

m_omutex()

templatevoid qsafequeue::push_back(const t &data)

templatet qsafequeue::pop_front()

針對上面的實現,考慮這樣一種情況,假設佇列很長,並且在某些時候從佇列中讀取資料的執行緒要遠遠多於向佇列中新增資料,此時讀執行緒的鎖就會影響寫執行緒,從而降低佇列的執行效率,為此我們需要改進安全佇列,使用讀寫兩個不同的鎖,看下列實現:

qsafequeue.h

#include #include templateclass qsafequeue

;

qsafequeue.cpp
templateqsafequeue::qsafequeue()

: m_olist(),

m_omutex()

templatevoid qsafequeue::push_back(const t &data)

templatet qsafequeue::pop_front()

qmutex有兩種模式:

recursive:這種模式下,要求lock和unlock必須同步出現,即呼叫了多少次lock就必須呼叫多少次unlock。

nonrecursive:這種模式下,只允許執行緒只能鎖一次,預設是這種模式。

qt提供了qmutexlocker來簡單的實現qmutex的鎖定與解鎖,可以將上面push_back方法改為如下**,pop方法同樣適用

templatevoid qsafequeue::push_back(const t &data)

在上面的實現中,當隊列為空時,我們直接丟擲了乙個異常,但有時我們希望當隊列為空時執行緒等待,直到有資料為止(這種佇列又稱為阻塞佇列)。這時我們就應該使用條件變數。qwaitcondition必須與qmutex配合使用不能單獨使用。看如下實現:

qblockingqueue.h

#include #include #include template class qblockingqueue

;

qblockingqueue.cpp
templateqblockingqueue::qblockingqueue()

: m_olist(),

m_omutex(),

m_owaitcondition()

// 首先判斷佇列是否為空,如果隊列為空時,當加入新的資料時,呼叫條件等待的wakeall喚醒

// 其他阻塞的讀執行緒,如果有多個讀執行緒,只能喚醒乙個執行緒,喚醒執行緒的順序一般先喚醒高優先順序的,

// 如果優先順序相同,則喚醒等待時間長的那個

templatevoid qblockingqueue::push_back(const t &data)

// 當隊列為空時等待,qwaitcondition還會釋放qmutex(內部會呼叫unlock)

// 其他呼叫此方法的執行緒也阻塞在等待條件這裡

templatet qblockingqueue::pop_front()

t temp = m_olist.front();

m_olist.pop_front();

m_omutex.unlock();

return temp;

}

在某些情況下(某些實時系統),當我們不能在一段時間內處理完資料,將丟棄這些資料,只處理最新的資料這時就需要對我們的阻塞佇列增加超時限制(過載的push_back和pop_front):

bool qblockingqueue::push_back(const t &data, const int milisec)

return false;

}templatet qblockingqueue::pop(const int milisec)

if (bresult)

}throw "time out";

}

還有一些情況,佇列不能無限大(例如倉庫的大小都是有限制的),這時我們就需要有大小的阻塞佇列,看如下新的實現:

qblockingqueue.h

#include #include #include template class qblockingqueue

;

qblockingqueue.cpp
templateqblockingqueue::qblockingqueue(int nmaxsize)

: m_olist(),

m_omutex(),

m_oreadwaitcondition(),

m_owritewaitcondition(),

m_nmaxsize(nmaxsize)

templatevoid qblockingqueue::push_back(const t &data)

m_olist.push_back(data);

m_omutex.unlock();

if (bempty)

m_oreadwaitcondition.wakeall();

} templatet qblockingqueue::pop_front()

t temp = m_olist.front();

m_olist.pop_front();

m_omutex.unlock();

if (bfull)

m_owritewaitcondition.wakeall();

return temp;

}

針對大多數的容器比如qlist,我們常用的操作都是讀取資料,一般插入和刪除操作都比較少,這時我們使用qmutex就會大大降低效率,此時我們就需要用讀寫鎖,看如下**:

qsafelist.h

#include #include template class qsafelist

;

qsafelist.cpp
template qsafelist::qsafelist()

: m_olist(),

m_orwlock()

template t qsafelist::at(int i)

t temp = m_olist[i];

m_orwlock.unlock();

return temp;

}

template t qsafelist::at(int i)

return m_olist[i];

}

訊號量有時又稱為訊號燈,更像一種更一般化的mutex,當訊號量管理的資源只有乙個時,與互斥量基本沒有區別,但是互斥量的lock和unlock必須在乙個執行緒內呼叫,而訊號量的aquire與release可以在不同的執行緒呼叫,因此訊號量還能夠起到同步執行緒的作用。

qt執行緒同步之訊號量

const int data size 100 const int buf size 10 int buf buf size qmutex mutex 控制線程間的互斥 freespace和usespace控制線程間的同步 控制可被生產者填充資料的那部分緩衝區 qsemaphore freespac...

Qt 多執行緒同步之互斥鎖

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

Qt 多執行緒同步之條件變數

qwaitcondition與qmutex結合,可以使乙個執行緒在滿足一定條件時通知其他多個執行緒,使它們及時作出響應,這樣比只使用互斥量效率要高一些。常用api 生產者 消費者模型 ifndef mythread h define mythread h include include includ...