POSIX執行緒 條件變數

2021-05-25 00:26:21 字數 2633 閱讀 1318

條件變數是 posix 執行緒結構,可以讓您在遇到某些條件時「喚醒」執行緒。可以將它們看作是一種執行緒安全的訊號傳送。

如果執行緒正在等待某個特定條件發生,它應該如何處理這種情況?它可以重複對互斥物件鎖定和解鎖,每次都會檢查共享資料結構,以查詢某個值。但這是在浪費時間和資源,而且這種繁忙查詢的效率非常低。解決這個問題的最佳方法是使用 pthread_cond_wait() 呼叫來等待特殊條件發生。了解 pthread_cond_wait() 的作用非常重要;它是 posix 執行緒訊號傳送系統的核心,也是最難以理解的部分。

首先,讓我們考慮以下情況:執行緒為檢視鍊錶而鎖定了互斥物件,然而該鍊錶恰巧是空的。這一特定執行緒什麼也幹不了:其設計意圖是從鍊錶中除去節點,但是現在卻沒有節點。因此,它只能:

鎖定互斥物件時,執行緒將呼叫 pthread_cond_wait(&cond, &mutex)。pthread_cond_wait() 呼叫相當複雜,因此我們每次只執行它的乙個操作。

pthread_cond_wait() 所做的第一件事就是同時對互斥物件解鎖(於是其它執行緒可以修改鍊錶),並等待條件 cond 發生(這樣當 pthread_cond_wait() 接收到另乙個執行緒的「訊號」時,它將甦醒)。現在互斥物件已被解鎖,其它執行緒可以訪問和修改鍊錶,可能還會新增項。

此 時,pthread_cond_wait() 呼叫還未返回。對互斥物件解鎖會立即發生,但等待條件cond 通常是乙個阻塞操作,這意味著執行緒將睡眠,在它甦醒之前不會消耗 cpu 週期。這正是我們期待發生的情況。執行緒將一直睡眠,直到特定條件發生,在這期間不會發生任何浪費 cpu 時間的繁忙查詢。從執行緒的角度來看,它只是在等待 pthread_cond_wait() 呼叫返回。

現在繼續說明,假設另乙個執行緒(稱作「2 號執行緒」)鎖定了mutex 並對鍊錶新增了一項。在對互斥物件解鎖之後,2 號執行緒會立即呼叫函式 pthread_cond_broadcast(&cond)。此操作之後,2 號執行緒將使所有等待 cond 條件變數的執行緒立即甦醒。這意味著第乙個執行緒(仍處於 pthread_cond_wait() 呼叫中)現在將甦醒。

現 在,看一下第乙個執行緒發生了什麼。您可能會認為在 2 號執行緒呼叫 pthread_cond_broadcast(&cond) 之後,1 號執行緒的 pthread_cond_wait() 會立即返回。不是那樣的!實際上,pthread_cond_wait() 將執行最後乙個操作:重新鎖定 mutex。一旦 pthread_cond_wait() 鎖定了互斥物件,那麼它將返回並允許 1 號執行緒繼續執行。那時,它可以馬上檢查鍊錶,檢視它所感興趣的更改。

整個過程執**況如下:

第乙個執行緒首先呼叫:

然後,它檢查了鍊錶。沒有找到感興趣的東西,於是它呼叫:

然後,pthread_cond_wait() 呼叫在返回前執行許多操作:

它對mutex 解鎖,然後進入睡眠狀態,等待 cond 以接收 posix 執行緒「訊號」。一旦接收到「訊號」(加引號是因為我們並不是在討論傳統的 unix 訊號,而是來自 pthread_cond_signal() 或 pthread_cond_broadcast() 呼叫的訊號),它就會甦醒。但 pthread_cond_wait() 沒有立即返回,它還要做一件事:重新鎖定 mutex:

pthread_cond_wait() 知道我們在查詢 mutex 「背後」的變化,因此它繼續操作,為我們鎖定互斥物件,然後才返回。

記住:在呼叫 pthread_cond_wait() 之前,pthread_cond_wait() 呼叫返回之後,互斥物件都是處於鎖定狀態。

條件變數是乙個需要初始化的真實資料結構。以下就是初始化的方法。首先,定義或分配乙個條件變數,如下所示:

然後,呼叫以下函式進行初始化:

在釋放或廢棄條件變數之前,需要毀壞它,如下所示:

一旦初始化了互斥物件和條件變數,就可以等待某個條件,如下所示:

請注意,**在邏輯上應該包含 cond 和 mutex。乙個特定條件只能有乙個互斥物件,而且條件變數應該表示互斥資料「內部」的一種特殊的條件更改。乙個互斥物件可以用於許多條件變數(例 如,cond_empty、cond_full、cond_cleanup),但每個條件變數只能有乙個互斥物件。

對於傳送訊號和廣播,需要注意一點。如果執行緒更改某些共享資料,而且它想要喚醒所有正在等待的執行緒,則應使用 pthread_cond_broadcast 呼叫,如下所示:

在某些情況下,活動執行緒只需要喚醒第乙個正在睡眠的執行緒。假設您只對佇列新增了乙個工作作業。那麼只需要喚醒乙個工作程式執行緒(再喚醒其它執行緒是不禮貌的!):

此函式只喚醒乙個執行緒。如果 posix 執行緒標準允許指定乙個整數,可以讓您喚醒一定數量的正在睡眠的執行緒,那就更完美了。

多執行緒工作組

在這個方案中,我們建立了許多任務作程式執行緒。每個執行緒都會檢查 wq(「工作佇列」),檢視是否有需要完成的工作。如果有需要完成的工作,那麼執行緒將從佇列中除去乙個節點,執行這些特定工作,然後等待新的工作到達。

與此同時,主線程負責建立這些工作程式執行緒、將工作新增到佇列,然後在它退出時收集所有工作程式執行緒。

需要佇列是出於兩個原因。首先,需要佇列來儲存工作作業。還需要可用於跟蹤已終止執行緒的資料結構。

POSIX執行緒 條件變數(一)

為什麼使用條件變數 condition variable 想想我們怎麼實現下面的場景 當執行緒a需要通知執行緒b某件事情已經準備好,我們該怎麼做?我們通常的做法是 設定乙個全域性變數v,如果執行緒a已經準備好了某件事,則把v設定為1 執行緒b則不停得檢測v,直到v變為1再繼續下面的操作。這種做法的缺...

posix多執行緒 條件變數

條件變數是用來通知共享資料狀態資訊的。1.條件變數初始化兩種方式 1 靜態初始化 pthread cond t cond pthread cond initializer 示例如下 include typedef struct my struct tag my struct t my struct ...

POSIX條件變數

條件變數 1.當乙個執行緒互斥地訪問某個變數時,他可能發現其他執行緒改變狀態之前,他什麼也不做了 2.例如 乙個執行緒訪問佇列時,發現隊列為空,他只能等待,只到其他執行緒將乙個節點新增到佇列中,這種情況就需要用到條件變數 對於解決無界緩衝區比較好的選擇 函式 pthread cond init初始化...