Linux多線程編程四(條件變量)

       前一節中咱們講述瞭如何使用互斥鎖來實現線程間數據的共享和通訊,互斥鎖一個明顯的缺點是它只有兩種狀態:鎖定和非鎖定。而條件變量經過容許線程阻塞和等待另外一個線程發送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一塊兒使用。使用時,條件變量被用來阻塞一個線程,當條件不知足時,線程每每解開相應的互斥鎖並等待條件發生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將從新鎖定互斥鎖並從新測試條件是否知足。通常說來,條件變量被用來進行線程間的同步。   數據結構


1. 相關操做函數


    條件變量和互斥鎖同樣,都有靜態動態兩種建立方式工具

    靜態方式測試

    靜態方式使用PTHREAD_COND_INITIALIZER常量,如: pthread_cond_t  cond = PTHREAD_COND_INITIALIZERspa

    動態方式線程

    int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)code

    使用 cond_attr 指定的屬性初始化條件變量 cond,當 cond_attr爲 NULL時,使用缺省的屬性。LinuxThreads實現條件變量不支持屬性,所以 cond_attr參數實際被忽略。對象

    註銷資源

    int pthread_cond_destroy(pthread_cond_t *cond)get

    註銷一個條件變量須要調用pthread_cond_destroy(),只有在沒有線程在該條件變量上等待的時候才能註銷這個條件變量,不然返回EBUSY。由於Linux實現的條件變量沒有分配什麼資源,因此註銷動做只包括檢查是否有等待線程。


2. 等待和激發


   等待

   int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

   這個函數是POSIX線程信號發送系統的核心,也是最難以理解的部分,過程爲:解鎖-wait-收到信號-加鎖-返回。

   設置時間的等待

   int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, conststruct timespec *abstime)

   pthread_cond_timedwait和 pthread_cond_wait同樣,自動解鎖互斥量及等待條件變量,但它還限定了等待時間。若是在 abstime指定的時間內 cond未觸發,互斥量 mutex被從新加鎖,並返回錯誤 ETIMEDOUT。abstime參數指定一個絕對時間,時間原點與 time和 gettimeofday相同:abstime = 0表示 1970-1- 1 00:00:00 GMT。 

   pthread_cond_timedwait 函數出錯時返回下列錯誤代碼:

   ETIMEDOUT   abstime 指定的時間超時時,條件變量未觸發

   EINTR       pthread_cond_timedwait 被觸發中斷


    激發

    int pthread_cond_signal(pthread_cond_t *cond);

    int pthread_cond_broadcast(pthread_cond_t *cond);

    激發條件有兩種形式,pthread_cond_signal()激活一個等待該條件的線程,多個線程阻塞在此條件變量上時,哪個線程被喚醒是由線程的調度策略所決定的;而pthread_cond_broadcast()則激活全部等待線程,這些線程被喚醒後將再次競爭相應的互斥鎖。

   要注意的是,必須用保護條件變量的互斥鎖來保護激活函數,不然條件知足信號有可能在測試條件和調用pthread_cond_wait()函數之間被髮出,從而形成無限制的等待。 


2、互斥量與條件變量

    問題:從本質上說互斥量就是一把鎖,互斥量串行執行,能確保每次只有一個線程訪問。互斥量是線程程序必需的工具,但它們並不是萬能的。例如,若是線程正在輪詢等待共享數據內某個條件出現,那會發生什麼呢?它能夠重複對互斥對象鎖定和解鎖,每次都會檢查共享數據結構,以查找某個值。但這是在浪費時間和資源,並且這種繁忙查詢的效率很是低。一樣,在每次檢查之間讓線程短暫地進入睡眠,好比睡眠3s,可是所以線程代碼就沒法最快做出響應。

    問題的解決: 條件變量經過容許線程阻塞和等待另外一個線程發送信號的方法彌補了互斥鎖的不足,條件變量常和互斥鎖一塊兒使用。使用時,條件變量被用來阻塞一個線程,當條件不知足時,線程每每解開相應的互斥鎖並等待條件發生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將從新鎖定互斥鎖並從新測試條件是否知足。


三 . 應用                                                                                     

    設有兩個共享的變量 x 和 y,經過互斥量 mut 保護,當 x > y 時,條件變量 cond 被觸發。

             int x,y;

             int x,y;

             pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

             pthread_cond_t cond = PTHREAD_COND_INITIALIZER;                                                                                       

    等待直到 x > y 的執行流程:

             pthread_mutex_lock(&mut);

              while (x <= y) {

                      pthread_cond_wait(&cond, &mut);

              }

              /* 對 x、y 進行操做 */

              pthread_mutex_unlock(&mut);                                                                                        

    對 x 和 y 的修改可能致使 x > y,應當觸發條件變量:                                                                                         

             pthread_mutex_lock(&mut);

              /* 修改 x、y */

              if (x > y) pthread_cond_broadcast(&cond);

              pthread_mutex_unlock(&mut);                                                                                      

       若是可以肯定最多隻有一個等待線程須要被喚醒(例如,若是隻有兩個線程經過 x、y 通訊),則使用 pthread_cond_signal 比 pthread_cond_broadcast 效率稍高一些。若是不能肯定,應當用 pthread_cond_broadcast。

       要等待在 5 秒內 x > y,這樣處理:                                                                                        

              struct timeval now;

              struct timespec timeout;

              int retcode;                                                                                      

              pthread_mutex_lock(&mut);

              gettimeofday(&now);

              timeout.tv_sec = now.tv_sec + 5;

              timeout.tv_nsec = now.tv_usec * 1000;

              retcode = 0;

              while (x <= y && retcode != ETIMEDOUT) {

                      retcode = pthread_cond_timedwait(&cond, &mut, &timeout);

              }

              if (retcode == ETIMEDOUT) {

                      /* 發生超時 */

              } else {

                      /* 操做 x 和  y */

              }

              pthread_mutex_unlock(&mut);

相關文章
相關標籤/搜索