QWaitCondition 的正確使用方法

2021-09-25 07:37:24 字數 2014 閱讀 9211

簡單用法

qwaitcondition 用於多執行緒的同步,乙個執行緒呼叫qwaitcondition::wait() 阻塞等待,直到另乙個執行緒呼叫qwaitcondition::wake() 喚醒才繼續往下執行。

為了描述方便,這裡假設主線程呼叫send()往通訊口傳送乙個資料報,然後阻塞等待回包才繼續往下執行。另乙個執行緒(通訊線程)不斷從通訊口中接收資料並解析成資料報,然後喚醒主線程。下面是按網上給的最簡單的方法:

// 示例一

// 主線程

send(&packet);

mutex.lock();

condition.wait(&mutex);

if (m_receivedpacket)

mutex.unlock();

// 通訊線程

m_receivedpacket = parsepacket(buffer);  // 將接收的資料解析成包

condition.wakeall();

通常情況下,上述**能跑得很好。但在某些特殊情況下,可能會出現混亂,大大降低通訊可靠性。

在主線程中,呼叫 send(&packet) 傳送後,假如通訊線程立即收到回包,在主線程還來不及呼叫 wait() 的時候,已經先 wakeall() 了,顯然這次喚醒是無效的,但主線程繼續呼叫 wait(),然後一直阻塞在那裡,因為該回的包已經回了。經測試出現這種現象的概率還是挺大的,因為我們不敢保證主線程總會被優先排程。即使主線程已經呼叫了 wait(),也不能保證底層作業系統的 wait_block 系統呼叫先於 wake 系統呼叫,畢竟wait() 函式也是層層封裝的。

嚴謹用法

qwaitcondition::wait() 在使用時必須傳入乙個上鎖的 qmutex 物件。這是很有必要的。而上述示例一**中,我們雖然用了 mutex,但只是為了形式上傳入qmutex引數,讓編譯器能正常編譯而已,事實上,沒有其它任何執行緒再用到這個mutex。而 mutex 本來就是讓多個執行緒能協調工作的,所以上述示例一主線程用的 mutex 是無效的。

根據 qt 手冊,wait() 函式必須傳入乙個已上鎖的 mutex 物件,在 wait() 執行過程中,mutex一直保持上鎖狀態,直到呼叫作業系統的wait_block 在阻塞的一瞬間把 mutex 解鎖(嚴格說來應該是原子操作,即系統能保證在真正執行阻塞等待指令時才解鎖)。另一線程喚醒後,wait() 函式將在第一時間重新給 mutex 上鎖(這種操作也是原子的),直到顯示呼叫 mutex.unlock() 解鎖。

在通訊線程也用上 mutex 後,整個通訊時序正常了,完全解決了示例一的問題。**如下:

// 示例二

// 主線程

mutex.lock();

send(&packet);

condition.wait(&mutex);

if (m_receivedpacket)

mutex.unlock();

// 通訊線程

m_receivedpacket = parsepacket(buffer);  // 將接收的資料解析成包

mutex.lock();

condition.wakeall();

mutex.unlock();

上述示例二中,主線程先把 mutex 鎖佔據,即從傳送資料報開始,一直到 qwaitcondition::wait() 在作業系統層次真正執行阻塞等待指令,這一段主線程的時間段內,mutex 一直被上鎖,即使通訊線程很快就接收到資料報,也不會直接呼叫 wakeall(),而是在呼叫 mutex.lock() 時阻塞住(因為主線程已經把mutex佔據上鎖了,再嘗試上鎖就會被阻塞),直到主線程 qwaitcondition::wait() 真正執行作業系統的阻塞等待指令並釋放mutex,通訊線程的 mutex.lock() 才即出阻塞,繼續往下執行,呼叫 wakeall(),此時一定能喚醒主線程成功。

由此可見,通過 mutex 把有嚴格時序要求的**保護起來,同時把 wakeall() 也用同乙個 mutex 保護起來,這樣能保證:一定先有 wait() ,再有 wakeall(),不管什麼情況,都能保證這種先後關係,而不至於擺烏龍。

推而廣之

QWaitCondition 的正確使用方法

喚醒執行緒 class worker public qthread 主線程 worker wt mutex.lock wt.start sleep 1000 qdebug main wait con.wait mutex qdebug main unwait mutex.unlock qdebug ...

QT中多執行緒QWaitCondition例子解析

include mainwindow.h include ui mainwindow.h include include include include qmutex mutex qwaitcondition waitcon int number 6 int count 0 void mythrea...

container of 的的的原理

另外一篇,同樣精彩,揭開linux核心中container of的神秘面紗 華清遠見嵌入式學院講師。在linux 核心中有乙個大名鼎鼎的巨集container of 這個巨集是用來幹嘛的呢?我們先來看看它在核心中是怎樣定義的。呵呵,乍一看不知道是什麼東東。我們先來分析一下container of p...