1. 程式人生 > >Linux系統程式設計—條件變數

Linux系統程式設計—條件變數

條件變數是用來等待執行緒而不是上鎖的,條件變數通常和互斥鎖一起使用。條件變數之所以要和互斥鎖一起使用,主要是因為互斥鎖的一個明顯的特點就是它只有兩種狀態:鎖定和非鎖定,而條件變數可以通過允許執行緒阻塞和等待另一個執行緒傳送訊號來彌補互斥鎖的不足,所以互斥鎖和條件變數通常一起使用。 當條件滿足的時候,執行緒通常解鎖並等待該條件發生變化,一旦另一個執行緒修改了環境變數,就會通知相應的環境變數喚醒一個或者多個被這個條件變數阻塞的執行緒。這些被喚醒的執行緒將重新上鎖,並測試條件是否滿足。一般來說條件變數被用於執行緒間的同步;當條件不滿足的時候,允許其中的一個執行流掛起和等待。 簡而言之,條件變數本身不是鎖,但它也可以造成執行緒阻塞,通常與互斥鎖配合使用,給多執行緒提供一個會合的場所。 **條件變數的優點:** 相較於mutex而言,條件變數可以減少競爭。如果僅僅是mutex,那麼,不管共享資源裡有沒資料,生產者及所有消費都全一窩蜂的去搶鎖,會造成資源的浪費。 如直接使用mutex,除了生產者、消費者之間要競爭互斥量以外,消費者之間也需要競爭互斥量,但如果匯聚(連結串列)中沒有資料,消費者之間競爭互斥鎖是無意義的。有了條件變數機制以後,只有生產者完成生產,才會引起消費者之間的競爭。提高了程式效率。 主要應用函式: pthread_cond_init函式 pthread_cond_destroy函式 pthread_cond_wait函式 pthread_cond_timedwait函式 pthread_cond_signal函式 pthread_cond_broadcast函式 以上6 個函式的返回值都是:成功返回0, 失敗直接返回錯誤號。 **pthread_cond_t型別:**用於定義條件變數,比如:pthread_cond_t cond; ##**pthread_cond_init函式** **函式原型:** int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); **函式作用:** 初始化一個條件變數 **引數說明:** **cond:**條件變數,呼叫時應傳&cond給該函式 **attr:**條件變數屬性,通常傳NULL,表示使用預設屬性 也可以使用靜態初始化的方法,初始化條件變數: pthread_cond_t cond = PTHREAD_COND_INITIALIZER; ##**pthread_cond_destroy函式** **函式原型:** int pthread_cond_destroy(pthread_cond_t *cond); **函式作用:** 銷燬一個條件變數 ##**pthread_cond_wait函式** **函式原型:** int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); **函式作用:** 阻塞等待一個條件變數。具體而言有以下三個作用: 1. 阻塞等待條件變數cond(參1)滿足; 2. 釋放已掌握的互斥鎖mutex(解鎖互斥量)相當於pthread_mutex_unlock(&mutex); 3. 當被喚醒,pthread_cond_wait函式返回時,解除阻塞並重新申請獲取互斥鎖 其中1、2.兩步為一個原子操作。 ##**pthread_cond_timedwait函式** **函式原型:** int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); **函式作用:** 限時等待一個條件變數 **引數說明:** 前兩個比較好理解,重點說明第三個引數。 這裡有個struct timespec結構體,可以在man sem_timedwait中檢視。結構體原型如下: struct timespec { ​ time_t tv_sec; /* seconds */ 秒 ​ long tv_nsec; /* nanosecondes*/ 納秒 } struct timespec定義的形參abstime是個絕對時間。注意,是絕對時間,不是相對時間。什麼是絕對時間?2018年10月1日10:10:00,這就是一個絕對時間。什麼是相對時間?給洗衣機定時30分鐘洗衣服,就是一個相對時間,也就是說從當時時間開始計算30分鐘,諸如此類。 如:time(NULL)返回的就是絕對時間。而alarm(1)是相對時間,相對當前時間定時1秒鐘。 adstime所相對的時間是相對於1970年1月1日00:00:00,也就是UNIX計時元年。 下面給出一個錯誤用法: struct timespec t = {1, 0}; pthread_cond_timedwait (&cond, &mutex, &t); 這種用法只能定時到 1970年1月1日 00:00:01秒,想必這個時間大家都還沒出生。 正確用法: time_t cur = time(NULL); 獲取當前時間。 struct timespec t; 定義timespec 結構體變數t t.tv_sec = cur+1; 定時1秒 pthread_cond_timedwait (&cond, &mutex, &t); 傳參 ##**pthread_cond_signal函式** **函式原型:** int pthread_cond_signal(pthread_cond_t *cond); **函式作用:** 喚醒至少一個阻塞在條件變數上的執行緒 ##**pthread_cond_broadcast函式** **函式原型:** int pthread_cond_broadcast(pthread_cond_t *cond); **函式作用:** 喚醒全部阻塞在條件變數上的執行緒 ##**生產者消費者條件變數模型** 不管是什麼語言,只要提到執行緒同步,一個典型的案例就是生產者消費者模型。在Linux環境下,藉助條件變數來實現這一模型,是比較常見的一種方法。 假定有兩個執行緒,一個模擬生產者行為,一個模擬消費者行為。兩個執行緒同時操作一個共享資源(一般稱之為匯聚),生產向其中新增產品,消費者從中消費掉產品。 看如下示例,使用條件變數模擬生產者、消費者問題: ``` #