1. 背景
多線程中經常需要使用到鎖(pthread_mutex_t)來完成多個線程之間的互斥操作。
但是互斥鎖有一個明顯到缺點: 只有兩種狀態,鎖定和非鎖定。
而條件變量則通過允許線程阻塞並等待另一個線程發送喚醒信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。
2. 條件變量涉及到的主要函數
2.1 pthread_cond_wait 線程阻塞在條件變量
int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex);
函數將解鎖mutex參數指向的互斥鎖,並使當前線程阻塞在cv參數指向的條件變量上。
被阻塞的線程可以被pthread_cond_signal函數,pthread_cond_broadcast函數喚醒,也可能在被信號中斷后被喚醒。
pthread_cond_wait函數返回時,相應的互斥鎖將被當前線程鎖定,即使是函數出錯返回。
pthread_cond_wait函數的返回並不意味着條件的值一定發生了變化,必須重新檢查條件的值。
最好的測試方法是循環調用pthread_cond_wait函數,並把滿足條件的表達式置為循環的終止條件。
1 pthread_mutex_lock(); 2 while (condition_is_false) 3 pthread_cond_wait(); 4 pthread_mutex_unlock();
阻塞在同一個條件變量上的不同線程被喚醒的次序是不一定的。
2.2 pthread_cond_signal 線程被喚醒
int pthread_cond_signal(pthread_cond_t *cv);
函數被用來釋放被阻塞在指定條件變量上的一個線程。
必須在互斥鎖的保護下使用相應的條件變量。否則對條件變量的解鎖有可能發生在鎖定條件變量之前,從而造成死鎖。
喚醒阻塞在條件變量上的所有線程的順序由調度策略決定,如果線程的調度策略是SCHED_OTHER類型的,系統將根據線程的優先級喚醒線程。
如果沒有線程被阻塞在條件變量上,那么調用pthread_cond_signal()將沒有作用。
更多函數可以看: http://blog.csdn.net/icechenbing/article/details/7662026
3. 實例代碼
實現功能: 2個線程對count每次分別加1, 第三個線程等count大於10后一次加100.
3.1 加1線程函數
1 void *inc_count(void *idp) 2 { 3 int i = 0; 4 int taskid = 0; 5 int *my_id = (int*)idp; 6 7 for (i=0; i<TCOUNT; i++) { 8 pthread_mutex_lock(&count_mutex); 9 taskid = count; 10 count++; 11 12 /* 13 喚醒一個阻塞在該條件變量到線程 14 如果沒有線程被阻塞在條件變量上,那么調用pthread_cond_signal()將沒有作用 15 */ 16 pthread_cond_signal(&count_threshold_cv); 17 18 printf("inc_count(): thread %d, count = %d, unlocking mutex\n", *my_id, count); 19 pthread_mutex_unlock(&count_mutex); 20 sleep(1); 21 } 22 printf("inc_count(): thread %d, Threshold reached.\n", *my_id); 23 24 pthread_exit(NULL); 25 }
3.2 count滿足條件后, 單次加100函數
1 void *watch_count(void *idp) 2 { 3 int *my_id = (int*)idp; 4 printf("Starting watch_count(): thread %d\n", *my_id); 5 6 pthread_mutex_lock(&count_mutex); 7 while(count<COUNT_LIMIT) { 8 sleep(3); 9 /* 10 函數將自動/原子的解鎖count_mutex參數指向的互斥鎖,並使當前線程阻塞在cv參數指向的條件變量上 11 被阻塞的線程可以被pthread_cond_signal函數,pthread_cond_broadcast函數喚醒,也可能在被信號中斷后被喚醒 12 pthread_cond_wait函數的返回並不意味着條件的值一定發生了變化,必須重新檢查條件的值. 13 本例子中線程被喚醒后, 仍然在while內會再次判斷COUNT_LIMIT是否滿足條件的值 14 pthread_cond_wait函數返回時,相應的互斥鎖將被當前線程鎖定,即使是函數出錯返回 15 */ 16 pthread_cond_wait(&count_threshold_cv, &count_mutex); 17 printf("watch_count(): thread %d Condition signal received.\n", *my_id); 18 } 19 20 count += 100; 21 pthread_mutex_unlock(&count_mutex); 22 pthread_exit(NULL); 23 }
3.3 整體代碼
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 #define NUM_THREADS 3 6 #define TCOUNT 10 7 #define COUNT_LIMIT 10 8 9 int count = 0; 10 int thread_ids[3] = {0,1,2}; 11 pthread_mutex_t count_mutex; 12 pthread_cond_t count_threshold_cv; 13 14 void *inc_count(void *idp) 15 { 16 int i = 0; 17 int taskid = 0; 18 int *my_id = (int*)idp; 19 20 for (i=0; i<TCOUNT; i++) { 21 pthread_mutex_lock(&count_mutex); 22 taskid = count; 23 count++; 24 25 /* 26 喚醒一個阻塞在該條件變量到線程 27 如果沒有線程被阻塞在條件變量上,那么調用pthread_cond_signal()將沒有作用 28 */ 29 pthread_cond_signal(&count_threshold_cv); 30 31 printf("inc_count(): thread %d, count = %d, unlocking mutex\n", *my_id, count); 32 pthread_mutex_unlock(&count_mutex); 33 sleep(1); 34 } 35 printf("inc_count(): thread %d, Threshold reached.\n", *my_id); 36 37 pthread_exit(NULL); 38 } 39 40 void *watch_count(void *idp) 41 { 42 int *my_id = (int*)idp; 43 printf("Starting watch_count(): thread %d\n", *my_id); 44 45 pthread_mutex_lock(&count_mutex); 46 while(count<COUNT_LIMIT) { 47 sleep(3); 48 /* 49 函數將自動/原子到解鎖mutex參數指向的互斥鎖,並使當前線程阻塞在cv參數指向的條件變量上 50 被阻塞的線程可以被pthread_cond_signal函數,pthread_cond_broadcast函數喚醒,也可能在被信號中斷后被喚醒 51 pthread_cond_wait函數的返回並不意味着條件的值一定發生了變化,必須重新檢查條件的值. 52 本例子中使用類COUNT_LIMIT最為滿足條件的值 53 pthread_cond_wait函數返回時,相應的互斥鎖將被當前線程鎖定,即使是函數出錯返回 54 */ 55 pthread_cond_wait(&count_threshold_cv, &count_mutex); 56 printf("watch_count(): thread %d Condition signal received.\n", *my_id); 57 } 58 59 count += 100; 60 pthread_mutex_unlock(&count_mutex); 61 pthread_exit(NULL); 62 } 63 64 int main (int argc, char *argv[]) 65 { 66 int i, rc; 67 pthread_t threads[3]; 68 pthread_attr_t attr; 69 70 /* Initialize mutex and condition variable objects */ 71 pthread_mutex_init(&count_mutex, NULL); 72 pthread_cond_init (&count_threshold_cv, NULL); 73 74 /* For portability, explicitly create threads in a joinable state */ 75 pthread_attr_init(&attr); 76 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 77 pthread_create(&threads[0], &attr, inc_count, (void *)&thread_ids[0]); 78 pthread_create(&threads[1], &attr, inc_count, (void *)&thread_ids[1]); 79 pthread_create(&threads[2], &attr, watch_count, (void *)&thread_ids[2]); 80 81 /* Wait for all threads to complete */ 82 for (i=0; i<NUM_THREADS; i++) { 83 pthread_join(threads[i], NULL); 84 } 85 printf ("Main(): Waited on %d threads. Done.\n", NUM_THREADS); 86 87 /* Clean up and exit */ 88 pthread_attr_destroy(&attr); 89 pthread_mutex_destroy(&count_mutex); 90 pthread_cond_destroy(&count_threshold_cv); 91 pthread_exit(NULL); 92 93 return 0; 94 }
