一直以来都天真的认为线程间同步的方法只有信号量,互斥量,邮箱,消息队列,知道最近开始研究一些Linux方面的代码才发现自己是多么的很傻很天真。在Linux中还存在这一种叫做条件变量的东西。必须承认我在理解这个概念上花了很多时间,查阅了很多资料。这里主要分析如下几个问题:1. 条件变量是什么;2.为什么要和互斥量配合使用,互斥量保护的是什么;3.为什么条件变量经常会和while配合使用。
条件变量是线程同步的一种手段。条件变量用来自动阻塞一个线程,直到条件(predicate)满足被触发为止。通常情况下条件变量和互斥锁同时使用。
条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个/多个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"信号。
这里让我们先用一段代码说明一般条件变量是如何和互斥量配合使用的:
1 int WaitForPredicate() 2 { 3 // lock mutex (means:lock access to the predicate) 4 pthread_mutex_lock(&mtx); 5 6 // we can safely check this, since no one else should be 7 // changing it unless they have the mutex, which they don‘t 8 // because we just locked it. 9 while (!predicate) 10 { 11 // predicate not met, so begin waiting for notification 12 // it has been changed *and* release access to change it 13 // to anyone wanting to by unlatching the mutex, doing 14 // both (start waiting and unlatching) atomically 15 pthread_cond_wait(&cv,&mtx); 16 } 17 18 // we own the mutex here. further, we have assessed the 19 // predicate is true (thus how we broke the loop). 20 21 // You *must* release the mutex before we leave. 22 pthread_mutex_unlock(&mtx); 23 }
那这个互斥量保护的是什么呢?是条件变量本身么?并不是!mutex使用来保护predicate。mutex被成功lock后我们就可以放心的去读取predicate的值,而不用担心在这期间predicate会被其他线程修改。如果predicate不满足条件,当前线程阻塞等待其他线程释放条件成立信号,并释放已经lock的mutex。这样一来其他线程就有了修改predicate的机会。当其他线程释放条件成立信号后,pthread_cond_wait函数返回,并再次lock mutex。
pthread_cond_wait的工作流程可以总结为:unlock mutex,start waiting -> lock mutex。
3. while的作用
在上面的代码当中可以看到,predicate是用while来检查的而不是用if在做判断。这样做的原因是,pthread_cond_wait的返回并不一定意味着其他线程释放了条件成立信号。而是意外返回。这种情况称为Spurious wakeup。之所以这样做的原因是从效率上考虑的。Volodya‘s blog - Spurious wakeups里有很详细的一个讲解,简单来说造成Spurious wakeup的原因在于,Linux中带阻塞功能的system call都会在进程收到了一个signal后返回。这就是为什么要用while来检查的原因。因为我们并不能保证wait函数返回就一定是条件满足,如果条件不满足,还需要继续等待。
1 #include <stdio.h> 2 #include <stdint.h> 3 #include <pthread.h> 4 #include <unistd.h> 5 6 static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER; 7 static pthread_cond_t cond_cv = PTHREAD_COND_INITIALIZER; 8 9 static unsigned char trigger_flag; 10 #define TRIGGER_CONDITION (5) 11 12 void* signal_thread(void *arg) 13 { 14 int i; 15 printf("enter signal thread\n"); 16 pthread_mutex_lock(&cond_mutex); 17 printf("signal thread start initialization\n"); 18 for (i=0; i<TRIGGER_CONDITION; ++i) 19 { 20 sleep(1); 21 } 22 printf("signal thread initialied\n"); 23 trigger_flag = 1; 24 pthread_cond_broadcast(&cond_cv); 25 pthread_mutex_unlock(&cond_mutex); 26 return 0; 27 } 28 29 void* wait_thread(void *arg) 30 { 31 printf("enter wait thread\n"); 32 pthread_mutex_lock(&cond_mutex); 33 printf("wait for trigger set\n"); 34 while (!trigger_flag) 35 { 36 pthread_cond_wait(&cond_cv, &cond_mutex); 37 } 38 printf("trigger set\n"); 39 pthread_mutex_unlock(&cond_mutex); 40 return 0; 41 } 42 43 44 int main(int argc, const char * argv[]) 45 { 46 int i; 47 pthread_attr_t attr; 48 pthread_t thread[2]; 49 pthread_attr_init(&attr); 50 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 51 pthread_create(&thread[0], &attr, wait_thread, NULL); 52 pthread_create(&thread[1], &attr, signal_thread, NULL); 53 /* Destory created 2 threads */ 54 for (i=0; i<2; ++i) 55 { 56 pthread_join(thread[i], NULL); 57 } 58 pthread_attr_destroy(&attr); 59 return 0; 60 }
原文:http://www.cnblogs.com/zhx831/p/3543633.html