条件变量(Condition Variables)
参考资料:http://game-lab.org/posts/posix-thread-cn/#5.1
条件变量是什么?
主线程
|
|
线程A
|
Thread B
|
主线程
|
创建和销毁条件变量
函数:
pthread_cond_init (condition,attr)
pthread_cond_destroy (condition)
pthread_condattr_init (attr)
pthread_condattr_destroy (attr)
用法:
- 静态初始化,像这样声明:pthread_con_t myconvar = PTHREAD_CON_INITIALIZER;
- 动态初始化,使用pthread_cond_init()函数。用创建条件变量的ID作为件参数传给线程,这种方法允许设置条件变量对象属性attr。
注意,不是所有的实现都用得着process-shared属性。
条件变量的等待和信号发送
函数:
pthread_cond_wait (condition,mutex)
pthread_cond_signal (condition)
pthread_cond_broadcast (condition)
使用:
- 在调用pthread_cond_wait()之前锁定互斥量失败,可致使其无法阻塞;
- 在调用pthread_cond_signal()之后解锁互斥量失败,则致使与之对应的pthread_cond_wait()函数无法完成,并仍保持阻塞状态。
注意事项:关于pthread_cond_signal和pthread_cond_wait函数存放位置问题:
pthread_cond_wait必须放在pthread_mutex_lock和pthread_mutex_unlock之间,因为他要根据共享变量的状态来觉得是否要等待,而为了不永远等待下去所以必须要在lock/unlock队中共享变量的状态改变必须遵守lock/unlock的规则。
pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有有缺点。
之间: pthread_mutex_lock xxxxxxx pthread_cond_signal pthread_mutex_unlock 缺点:在某些线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的行为),所以一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。 所以在Linux中推荐使用这种模式。 之后: pthread_mutex_lock xxxxxxx pthread_mutex_unlock pthread_cond_signal 优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了 缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。 所以,在Linux下最好pthread_cond_signal放中间,但从编程规则上说,其他两种都可以
实例分析
1 /****************************************************************************** 2 * 描述: 3 * 应用Pthreads条件变量的实例代码,主线程创建三个线程,其中两个为“count”变量做 4 * 加法运算,第三个线程监视“count”的值。当“count”达到一个限定值,等待线程准备接收来 5 * 自于两个加法线程中一个的信号,等待 线程唤醒后更改“count”的值。程序继续运行直到加法 6 * 线程达到TCOUNT的值。最后,主程序打印出count的值。 7 ******************************************************************************/ 8 #include <pthread.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <Windows.h> 12 13 #define NUM_THREADS 3 //线程个数 14 #define TCOUNT 5 //单线程轮询次数 15 #define COUNT_LIMIT 7 //发送信号的次数 16 int count = 0; //全局的累加量 17 18 pthread_mutex_t count_mutex; 19 pthread_cond_t count_threshold_cv; 20 21 void *inc_count(void *t) 22 { 23 long my_id = (long) t; 24 25 for (int i = 0; i < TCOUNT; i++) 26 { 27 pthread_mutex_lock(&count_mutex); 28 count++; 29 /* 30 * 检查count的值,如果条件满足就发信号给等待线程 31 * 注意,此处是用信号量锁定的。 32 * */ 33 if (count < COUNT_LIMIT) 34 { 35 printf("inc_count(): thread %ld, count = %d Threshold reached. ", 36 my_id, count); 37 pthread_cond_signal(&count_threshold_cv); 38 /* pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行. 39 * 如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。 40 * */ 41 printf("Just sent signal.\n"); 42 } 43 printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", my_id, 44 count); 45 pthread_mutex_unlock(&count_mutex); 46 47 /*为线程轮询互斥锁增加延时*/ 48 Sleep(1000); 49 } 50 pthread_exit(NULL); 51 return 0; 52 } 53 54 void *watch_count(void *t) 55 { 56 long my_id = (long) t; 57 printf("Starting watch_count(): thread %ld\n", my_id); 58 59 /************************************************************************/ 60 /* 为什么要与pthread_mutex 一起使用呢? */ 61 /* 这是为了应对线程1在调用pthread_cond_wait()但线程1还没有进入wait cond */ 62 /* 的状态的时候,此时线程2调用了cond_singal的情况。如果不用mutex锁的话, */ 63 /* 这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex 被释放 */ 64 /*(也就是 pthread_cod_wait() 进入wait_cond状态 并自动释放mutex)的时 */ 65 /* 候才能调用cond_singal(前提:线程2也使用mutex)。 */ 66 /************************************************************************/ 67 68 69 /* 锁定互斥量并等待信号,注意,pthread_cond_wait函数在等待时将自动以自动原子方式 70 * 解锁互斥量。还有,请注意,如果等待线程运行到等待函数之前已经满足COUNT_LIMIT的 71 * 条件判断,轮询会忽略掉等待函数, 72 * */ 73 while (count < COUNT_LIMIT) 74 { 75 pthread_mutex_lock(&count_mutex); 76 printf("watch_count(): thread %ld going into wait...\n", my_id); 77 pthread_cond_wait(&count_threshold_cv, &count_mutex); 78 /* pthread_cond_wait函数一进入wait状态就会自动release mutex,一旦wait成功获得 79 * cond条件变量后会自动lock mutex 80 * */ 81 printf("watch_count(): thread %ld Condition signal received.\n", my_id); 82 83 printf("watch_count(): thread %ld count now = %d.\n", my_id, count); 84 pthread_mutex_unlock(&count_mutex); 85 } 86 pthread_exit(NULL); 87 return 0; 88 } 89 90 int main(int argc, char *argv[]) 91 { 92 int i; 93 long t1 = 1, t2 = 2, t3 = 3; 94 pthread_t threads[3]; 95 pthread_attr_t attr; 96 97 /*初始化互斥量和条件变量对象*/ 98 pthread_mutex_init(&count_mutex, NULL); 99 pthread_cond_init(&count_threshold_cv, NULL); 100 101 /*创建线程时设为可连接状态,便于移植*/ 102 pthread_attr_init(&attr); 103 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // 可以省略,线程默认属性也是joinable 104 pthread_create(&threads[0], &attr, watch_count, (void *) t1); 105 pthread_create(&threads[1], &attr, inc_count, (void *) t2); 106 pthread_create(&threads[2], &attr, inc_count, (void *) t3); 107 108 /* 等待所有线程完成*/ 109 for (i = 1; i < NUM_THREADS; i++) 110 { 111 pthread_join(threads[i], NULL); 112 } 113 /*发送信号给监听线程*/ 114 pthread_cond_signal(&count_threshold_cv); 115 pthread_join(threads[0],NULL); 116 printf("Main(): Waited on %d threads. Final value of count = %d. Done.\n", 117 NUM_THREADS, count); 118 119 /*清除并退出 */ 120 pthread_attr_destroy(&attr); 121 pthread_mutex_destroy(&count_mutex); 122 pthread_cond_destroy(&count_threshold_cv); 123 124 system("pause"); 125 pthread_exit(NULL); 126 }
输出结果:
Starting watch_count(): thread 1 inc_count(): thread 2, count = 1 Threshold reached. Just sent signal. inc_count(): thread 2, count = 1, unlocking mutex inc_count(): thread 3, count = 2 Threshold reached. Just sent signal. inc_count(): thread 3, count = 2, unlocking mutex watch_count(): thread 1 going into wait... inc_count(): thread 2, count = 3 Threshold reached. Just sent signal. inc_count(): thread 2, count = 3, unlocking mutex inc_count(): thread 3, count = 4 Threshold reached. Just sent signal. inc_count(): thread 3, count = 4, unlocking mutex watch_count(): thread 1 Condition signal received. watch_count(): thread 1 count now = 4. watch_count(): thread 1 going into wait... inc_count(): thread 2, count = 5 Threshold reached. Just sent signal. inc_count(): thread 2, count = 5, unlocking mutex watch_count(): thread 1 Condition signal received. watch_count(): thread 1 count now = 5. watch_count(): thread 1 going into wait... inc_count(): thread 3, count = 6 Threshold reached. Just sent signal. inc_count(): thread 3, count = 6, unlocking mutex watch_count(): thread 1 Condition signal received. watch_count(): thread 1 count now = 6. watch_count(): thread 1 going into wait... inc_count(): thread 2, count = 7, unlocking mutex inc_count(): thread 3, count = 8, unlocking mutex inc_count(): thread 2, count = 9, unlocking mutex inc_count(): thread 3, count = 10, unlocking mutex watch_count(): thread 1 Condition signal received. watch_count(): thread 1 count now = 10. Main(): Waited on 3 threads. Final value of count = 10. Done. 请按任意键继续. . .