条件变量用于等待线程而不是加锁,条件变量通常与互斥量一起使用。条件变量之所以和互斥量一起使用,主要是因为互斥量一个明显的特点就是只有锁定和解锁两种状态,而条件变量可以通过让线程阻塞等待另一个线程发送信号来使用弥补了互斥锁的不足,所以互斥锁和条件变量通常一起使用。当条件满足时,线程通常会解锁并等待条件改变。一旦另一个线程修改了环境变量,它会通知相应的环境变量唤醒一个或多个被条件变量阻塞的线程。这些被唤醒的线程会重新获取锁并测试是否满足条件。通常条件变量用于线程之间的同步;允许其中一个执行流在不满足条件时暂停并等待。总之,条件变量本身不是锁,但它也会造成线程阻塞,通常与互斥锁一起使用,为多线程提供会面场所。条件变量的优点:与mutex相比,条件变量可以减少竞争。如果只是一个互斥锁,那么无论共享资源中是否有数据,生产者和所有消费者都会争先恐后地去抢锁,造成资源浪费。如果直接使用mutex,除了生产者和消费者之间的竞争,消费者还需要竞争mutex,但是如果聚合(链表)中没有数据,消费者之间竞争mutex是没有意义的。有了条件变量机制,只有当生产者完成生产时,才会引起消费者之间的竞争。提高了程序效率。主要应用函数:pthread_cond_init函数、pthread_cond_destroy函数、pthread_cond_wait函数、pthread_cond_timedwait函数、pthread_cond_signal函数、pthread_cond_broadcast函数。以上六个函数的返回值分别是:成功返回0,失败直接返回一个错误号。pthread_cond_t类型:用于定义条件变量,如:pthread_cond_tcond;##pthread_cond_init函数函数原型:intpthread_cond_init(pthread_cond_strictcond,constpthread_condattr_trestrictattr);functionattr:条件变量属性,一般传NULL,表示默认属性也可以用于静态初始化,初始化条件变量:pthread_cond_tcond=PTHREAD_COND_INITIALIZER;##pthread_cond_destroy函数函数原型:intpthread_cond_destroy(pthread_cond_t*cond);function函数:销毁一个Condition变量##pthread_cond_wait函数函数原型:intpthread_cond_wait(pthread_cond_trestrictcond,pthread_mutex_trestrictmutex);功能:阻塞等待条件变量。具体来说,它有以下三个功能:阻塞等待条件变量cond(参考1)满足;释放已经掌握的mutex(unlockingthemutex)相当于pthread_mutex_unlock(&mutex);唤醒后,pthread_cond_wait函数返回,Unblock并重新申请获取互斥量。步骤1和2是一个原子操作。##pthread_cond_timedwait函数函数原型:intpthread_cond_timedwait(pthread_cond_trestrictcond,pthread_mutex_restrictmutex,conststructtimespec*restrictabstime);功能:限时等待一个条件变量参数说明:前两个比较容易理解,重点强调第三个参数。这里是一个structtimespec结构体,可以在mansem_timedwait中查看。结构体原型如下:structtimespec{time_ttv_sec;/seconds/secondlongtv_nsec;/nanosecondes/nanosecond}structtimespec定义的形参abstime是一个绝对时间。注意是绝对时间,不是相对时间。什么是绝对时间?2018年10月1日10:10:00,这是一个绝对时间。什么是相对时间?洗衣机定时30分钟洗衣服,这是一个相对时间,也就是说从当前时间算起30分钟,以此类推。例如:time(NULL)返回绝对时间。而alarm(1)是一个相对时间,设置为相对于当前时间1秒。adstime的相对时间是相对于1970年1月1日的00:00:00,也就是UNIX计时元年。以下是错误的用法:structtimespect={1,0};pthread_cond_timedwait(&cond,&mutex,&t);这个用法只能计时到1970年1月1日的00:00:01秒。想必大家Neither都还没出生吧。正确用法:time_tcur=time(NULL);获取当前时间。结构timespect;定义timespec结构变量tt.tv_sec=cur+1;时间1秒pthread_cond_timedwait(&cond,&mutex,&t);传参##pthread_cond_signal函数函数原型:intpthread_cond_signal(pthread_cond_t*cond);函数功能:唤醒至少一个线程阻塞在条件变量上##pthread_cond_broadcast函数函数原型:intpthread_cond_broadcast(pthread_cond_t*cond);功能:唤醒阻塞在条件变量上的所有线程##Producerconsumer条件变量模型无论什么语言,只要涉及到线程同步,一个典型的案例就是生产者消费者模型。在Linux环境下,借助条件变量来实现这个模型是一种比较常见的方法。假设有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程在共享资源(通常称为池)上同时运行,生产者从中添加产品,消费者从中消费产品。请参见以下示例,使用条件变量来模拟生产者和消费者:}msg_t;msg_t*head=NULL;msg_t*mp=NULL;/*条件变量和互斥体的静态初始化*/pthread_cond_thas_product=PTHREAD_COND_INITIALIZER;pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;void*th_producer(void*arg){while(1){mp=malloc(sizeof(msg_t));mp->num=rand()%1000;//模拟生产一个产品printf("---produce:%d--------\n",mp->num);pthread_mutex_lock(&mutex);mp->next=head;head=mp;pthread_mutex_unlock(&mutex);pthread_cond_signal(&has_product);//唤醒线程消费productsleep(rand()%5);}returnNULL;}void*th_consumer(void*arg){while(1){pthread_mutex_lock(&mutex);while(head==NULL){//如果链表中没有product,则不需要抢锁,它已经被阻塞等待pthread_cond_wait(&has_product,&mutex);}mp=head;head=mp->next;//模拟消费一个产品pthread_mutex_unlock(&mutex);printf("=========consume:%d======\n",mp->num);free(mp);mp=NULL;sleep(rand()%5);}returnNULL;}intmain(){pthread_tpid,cid;srand(time(NULL));pthread_create(&pid,NULL,th_producer,NULL);pthread_create(&cid,NULL,th_consumer,NULL);pthread_join(pid,NULL);pthread_join(cid,NULL);return0;}运行结果:本文经授权转载自公众号《良旭Linux》世界500强外企Linux开发工程师良旭,在分享大量Linux干货,欢迎关注!
