線程同步——條件變量+鎖

參考博文:https://www.cnblogs.com/zhangxuan/p/6526854.html

簡介

條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:

  • 一個線程等待"條件變量的條件成立"而掛起;
  • 另一個線程使"條件成立"(給出條件成立信號)。

與互斥鎖不同,條件變量是用來等待而不是用來鎖住資源的。但它必須配合互斥鎖來使用。如果條件爲假,一個線程自動阻塞,並釋放互斥鎖,以使其他線程能夠獲得鎖來改變條件。

pthread_cond_wait() 是原子調用:等待條件變量,解除鎖,然後阻塞。
pthread_cond_wait() 返回,代表條件變量有信號,同時上鎖。

等待條件有兩種方式:條件等待 pthread_cond_wait() 和計時等待pthread_cond_timedwait(),其中計時等待方式如果在給定時刻前條件沒有滿足,則返回 ETIMEOUT。

無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或 pthread_cond_timedwait(),下同)。

激發條件有兩種方式:pthread_cond_signal() 激活一個等待該條件的線程,存在多個等待線程時按入隊順序激活其中一個;
pthread_cond_broadcast() 則激活所有等待線程(驚羣)。

條件變量的 API

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

使用 cond_attr 指定的屬性初始化條件變量 cond,當 cond_attrNULL 時,使用缺省的屬性。LinuxThreads 實現條件變量不支持屬性,
因此 cond_attr 參數實際被忽略。pthread_cond_t 類型的變量也可以用 PTHREAD_COND_INITIALIZER 常量進行靜態初始化。

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

調用 pthread_cond_signal()後要立刻釋放互斥鎖,因爲 pthread_cond_wait() 的最後一步是要將指定的互斥量重新鎖住,如果 pthread_cond_signal() 之後沒有釋放互斥鎖,pthread_cond_wait() 仍然要阻塞。
pthread_cond_signal() 方法使等待條件變量的一個線程重新開始。如果沒有等待的線程,則什麼也不做。如果有多個線程在等待該條件,只有一個能啓動。在多處理器上,該函數可能同時喚醒多個線程。
pthread_cond_broadcast() 重啓等待該條件變量的所有線程(驚羣)。如果沒有等待的線程,則什麼也不做。

使用 pthread_cond_signal() 不會有“驚羣”現象產生,它最多隻給一個線程發信號。假如有多個線程正在阻塞等待着這個條件變量的話,那麼是根據各等待線程優先級的高低確定哪個線程接收到信號開始繼續執行。
如果各線程優先級相同,則根據等待時間的長短來確定哪個線程獲得信號。但無論如何一個 pthread_cond_signal() 調用最多發信一次。

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

pthread_cond_wait() 自動解鎖互斥量,並等待條件變量觸發。這時線程掛起,不佔用 CPU 時間,直到條件變量被觸發。在調用 pthread_cond_wait() 之前,應用程序必須加鎖互斥量。
pthread_cond_wait() 函數返回前,自動重新對互斥量加鎖。
pthread_cond_timedwait()pthread_cond_wait() 一樣,但它還限定了等待時間。
互斥量的解鎖和在條件變量上掛起都是自動進行的。因此,在條件變量被觸發前,如果所有的線程都要對互斥量加鎖,這種機制可保證在線程加鎖互斥量和進入等待條件變量期間,條件變量不被觸發。
wait 方法流程一覽:手動對互斥量加鎖 → wait 解鎖並阻塞本線程 → 條件變量在別的線程中被 signal 觸發 → (如果互斥鎖被其他線程加鎖,阻塞等到解鎖)對互斥量加鎖 → wait 返回。

int pthread_cond_destroy(pthread_cond_t *cond);

銷燬一個條件變量,釋放它擁有的資源。進入 pthread_cond_destroy() 之前,必須沒有在該條件變量上等待的線程。否則返回 EBUSY。在 LinuxThreads 的實現中,條件變量不聯結資源,除檢查有沒有等待的線程外,pthread_cond_destroy() 實際上什麼也不做。

條件變量爲什麼一定要配合鎖使用

這是爲了應對線程 1 在調用 pthread_cond_wait() 但還沒有進入 wait cond 的狀態的時候,此時線程 2 觸發了 cond_singal 的情況。
如果不用鎖的話,這個 cond_singal 就丟失了。加了鎖的情況是,線程 2 必須等到鎖被釋放(也就是 pthread_cod_wait() 釋放鎖並進入 wait_cond 狀態 ,此時線程 2 上鎖)的時候才能去觸發 cond_singal

取消點

pthread_cond_wait()pthread_cond_timedwait() 都被實現爲取消點。因此,線程被取消之後,在該處等待的線程將立即重新運行,在重新鎖定 mutex 後 wait 函數返回,然後執行取消動作。

也就是說,如果 wait 函數被取消,mutex 將依然保持鎖定狀態,那麼線程需要定義退出回調函數來爲其解鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章