1. 程式人生 > >四十一、Linux 線程——線程同步之條件變量

四十一、Linux 線程——線程同步之條件變量

調用 eight attr_ linux 線程阻塞 out span 例子 信號

41.1 概念

41.1.1 條件變量的介紹

  • 互斥鎖的缺點是它只有兩種狀態:鎖定和非鎖定
  • 條件變量通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足
  • 條件變量內部是一個等待隊列,放置等待的線程,線程在條件變量上等待和通知,互斥鎖用來保護等待隊列(對等待隊列上鎖),條件變量通常和互斥鎖一起使用
  • 條件變量允許線程等待特定條件發生,當條件不滿足時,線程通常先進入阻塞狀態,等待條件發生變化。一旦其它的某個線程改變了條件,可喚醒一個或多個阻塞的線程
  • 具體的判斷條件還需用戶給出
  • 條件變量數據類型: pthread_cond_t

41.1.2 條件變量創建和銷毀

1 #include <pthread.h>
2
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr); 3 int pthread_cond_destroy(pthread_cond_t *cond);
  • 函數參數:
    • cond:條件變量
    • attr:條件變量屬性
  • 返回值:
    • 成功,返回 0;出錯,返回錯誤編號

41.1.3 條件變量等待操作

1 #include <pthread.h>
2 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
3 int pthread_cond_timewait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout); 4 5 struct timespec { 6 time_t tv_sec; /** seconds */ 7 ong tv_nsec; /** nanoseconds */ 8 };
  • 函數參數:
    • cond:條件變量
    • mutex:互斥鎖
  • 返回值:成功,返回 0,出錯,返回錯誤編號
  • 互斥鎖 mutex 是對條件變量 cond 的保護
  • 線程由於調用 wait 函數阻塞,否則釋放互斥鎖

41.1.4 條件變量通知操作

1 #include <pthread.h>
2 int pthread_cond_signal(pthread_cond_t *cond);
3 int pthread_cond_broadcast(pthread_cond_t *cond);
  • 函數參數:
    • cond:條件變量
  • 返回值:成功,返回 0;失敗,返回錯誤編號
  • 當條件滿足,線程需要通知等待的線程
  • pthread_cond_signal 函數通知單個線程
  • pthread_cond_broadcast 函數通知所有線程

41.1.5 pthread_cond_wait(cond, mutex) 函數內部流程

  1. unlock(&mutex):釋放鎖
  2. lock(&mutex)
  3. 將線程自己插入到條件變量的等待隊列中
  4. unlock(&mutex);
  5. 當前等待的線程阻塞 <<== 等待其他線程通知喚醒(signal 或 broadcast)
  6. 在喚醒後,lock(&mutex)
  7. 從等待隊列中刪除線程自己

41.2 例子

  一個線程負責計算結果,一個線程負責獲取結果

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 #include <unistd.h>
 5 
 6 /** 兩個線程定義的共享資源 */
 7 typedef struct {
 8     int res;
 9     int is_wait;    ///< 用戶給出的用於判斷的條件
10     pthread_cond_t cond;    ///< 條件變量
11     pthread_mutex_t mutex;  ///< 互斥鎖
12 }Result;
13 
14 
15 /** 計算並將結果放置在 Result 中的線程運行函數 */
16 void *set_fn(void *arg)
17 {
18     Result *r = (Result *)arg;
19     int i = 0;
20     int sum = 0;
21 
22     for(; i <= 100; i++){
23         sum += i;
24     }
25 
26     /** 將結果放置到 Result 中 */
27     r->res = sum;
28 
29     pthread_mutex_lock(&r->mutex);
30     /** 判斷獲取結果的線程是否準備好 */
31     while(!r->is_wait){
32         pthread_mutex_unlock(&r->mutex);
33         usleep(100);
34         pthread_mutex_lock(&r->mutex);
35     }
36     pthread_mutex_unlock(&r->mutex);
37 
38     /** 通知喚醒等待的那個獲取結果的線程 */
39     pthread_cond_broadcast(&r->cond);
40 
41     return (void *)0;
42 }
43 
44 /** 獲得結果的線程運行函數 */
45 void *get_fn(void *arg)
46 {
47     Result *r = (Result *)arg;
48 
49     /** 對兩個線程共享的判斷條件進行保護(加鎖) */
50     /** 兩個線程對判斷條件的操作是互斥的 */
51     pthread_mutex_lock(&r->mutex);
52     /** 當線程啟動後,將此變量設置為1,代表此線程已經準備好了 */
53     r->is_wait = 1;
54 
55     /** 獲取結果的線程等待 */
56     pthread_cond_wait(&r->cond, &r->mutex);
57 
58     /** 被喚醒後 */
59     pthread_mutex_unlock(&r->mutex);
60 
61     /** 去獲取計算結果 */
62     int res = r->res;
63     printf("0x%lx get sum is %d\n", pthread_self(), res);
64 
65     return (void *)0;
66 }
67 
68 int main(void)
69 {
70     int err;
71     pthread_t cal, get;
72 
73     Result r;
74     r.is_wait = 0;
75     pthread_cond_init(&r.cond, NULL);
76     pthread_mutex_init(&r.mutex, NULL);
77 
78     /** 啟動獲取結果的線程 */
79     if((err = pthread_create(&get, NULL, get_fn, (void *)&r)) != 0){
80         perror("pthread create error");
81     }
82 
83     /** 啟動計算結果的線程 */
84     if((err = pthread_create(&cal, NULL, set_fn, (void *)&r)) != 0){
85         perror("pthread create error");
86     }
87 
88     pthread_join(cal, NULL);
89     pthread_join(get, NULL);
90 
91     pthread_cond_destroy(&r.cond);
92     pthread_mutex_destroy(&r.mutex);
93 
94     pthread_cond_destroy(&r.cond);
95     pthread_mutex_destroy(&r.mutex);
96     return 0;
97 }

  編譯執行結果:

  技術分享圖片

四十一、Linux 線程——線程同步之條件變量