传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。 如下代码是apue的例子 pthread_cond_wait分析需要解决如下几个问题 pthread_cond_wait函数的实现是 这里线程1和2都wait在cond上,但是这个pthread_cond_signal本身只是为了唤醒线程1! 如上述代码所示,如果线程2没有对条件变量加锁,则线程2可能在线程1进入wait之前,发送信号,线程1还没有进入等待队列,所以信号丢失了! 在以前分析线程池的时候,写过这种代码 这里如果起了4个线程,且num == 0时,这cpu在4个线程之间来回调度,不停的解锁上锁,非常耗费cpu资源!如果采用pthread_cond_wait,则把线程加入等待队列,cpu并不调度,降低cpu使用。同时,pthread_cond_wait可以保证加入等待队列和解锁之间是原子的 栗子 此时没有惊群,只唤醒了一个线程1、pthread_cond_wait与signal函数
#include <pthread.h> int pthread_cond_wait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex ); int pthread_cond_timedwait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout ); 两者的返回值都是:若成功则返回0,否则返回错误编号 #include <pthread.h> int pthread_cond_signal( pthread_cond_t *cond ); int pthread_cond_broadcast( pthread_cond_t *cond ); 两者的返回值都是:若成功则返回0,否则返回错误编号 复制代码
pthread_cond_signal函数将唤醒等待该条件的某个线程
pthread_cond_broadcast函数将唤醒等待该条件的所有线程。
POSIX规范为了简化实现,允许pthread_cond_signal在实现的时候可以唤醒不止一个线程。
这里与我之前分析过的linux内核wait的实现方式不尽相同,内核wait的唤醒wakeup是唤醒最新加入wq上的_waiter,一次唤醒一个,如果唤醒会先再次加到等待队列,如果condition满足,则break,且从等待队列删除附上链接
这里要注意,mutex保护的条件并不是函数的参数cond,而是while循环判断的条件,while (workq == NULL),cond本身只是pthread_cond_t 变量,当pthread_cond_signal运行时,会唤醒等待在这个cond上的一个线程,如果在多处理器中可能唤醒多个线程产生惊群。2、深入分析
#include <pthread.h> struct msg { struct msg *m_next; /* value...*/ }; struct msg* workq; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; void process_msg() { struct msg* mp; for (;;) { pthread_mutex_lock(&qlock); while (workq == NULL) { pthread_cond_wait(&qread, &qlock); } mq = workq; workq = mp->m_next; pthread_mutex_unlock(&qlock); /* now process the message mp */ } } void enqueue_msg(struct msg* mp) { pthread_mutex_lock(&qlock); mp->m_next = workq; workq = mp; pthread_mutex_unlock(&qlock); /** 此时另外一个线程在signal之前,执行了process_msg,刚好把mp元素拿走*/ pthread_cond_signal(&qready); /** 此时执行signal, 在pthread_cond_wait等待的线程被唤醒, 但是mp元素已经被另外一个线程拿走,所以,workq还是NULL ,因此需要继续等待*/ }
上面这几个问题是我在看apue的时候不解的,后续在查阅资料和自己分析之后,得出如下结论。2.1 pthread_cond_wait()为什么必须要加while循环
当pthread_cond_wait(cond,mutex)之后,被调度走,此时是被lock的状态,当signal(cond)运行,可能会产生惊群效应,因为posix为了实现简单,在多处理函数中,会将多个等待cond的线程从等待队列移除,进入就绪态。那么如果用if,则都会跳出循环,而用while会加锁后再次判断是不是需要的条件!==cond本身只是标志,实际的条件还是while循环的判断条件!==附上man链接pthread_cond_signal(3) – Linux man page thread1: if (0<a<10) { pthread_cond_wait(&qread, &qlock); } thread2: if (0<a<5) { pthread_cond_wait(&qread, &qlock); }
pthread_mutex_lock(&qlock); a=6; pthread_mutex_unlock(&qlock); /** 此时另外一个线程在signal之前,执行了process_msg,刚好把mp元素拿走*/ pthread_cond_signal(&qready);
2.2 pthread_cond_signal之前的条件为什么要上锁
thread1 thread2 pthread_mutex_lock(&qlock); while (workq == NULL) { //pthread_mutex_lock(&qlock); workq == (void*)1; //pthread_mutex_unlock(&qlock); pthread_cond_signal(&qready); pthread_cond_wait(&qread, &qlock); } pthread_mutex_unlock(&qlock);
这里实际可以理解为与读写锁类似,读写是互斥的,线程2是写,线程1为读2.3 pthread_cond_wait的优势是什么
thread1 pthread_mutex_lock(&mut_num); while(num == 0) { pthread_mutex_unlock(&mut_num); sched_yield(); //schedule pthread_mutex_lock(&mut_num); } if(num>0) { i = num; num = 0; pthread_mutex_unlock(&mut_num); fprintf(stderr,"the num of thread %d,and the %d th is %dn",pthread_self(),(int)i); } else { pthread_mutex_unlock(&mut_num);//这里要注意,如果在临界区,跳转出临界区需要解锁再跳转 break; }
pthread_mutex_lock(&mut_num); while(num == 0) { pthread_cond_wait(&cond, &qlock); //当num>0时,pthread_cond_signal(&cond) } ... pthread_mutex_unlock(&mut_num);
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> pthread_mutex_t count_lock; pthread_cond_t count_ready; int count; void *decrement_count(void *arg) { pthread_mutex_lock(&count_lock); printf("decrement:waiting %dn",pthread_self()); /*等待满足条件,期间互斥量仍然可用*/ // while (count == 0) pthread_cond_wait(&count_ready, &count_lock); printf("decrement:count = %d,%dn", count,pthread_self()); if (count == 0) { printf("exit count:%dn",pthread_self()); //break; } count = 0; pthread_mutex_unlock(&count_lock); pthread_exit(NULL); } void *increment_count(void *arg) { pthread_mutex_lock(&count_lock); printf("increment:runningn"); count = 1; /*通知线程条件已满足*/ printf("increment:count = %dn", count); pthread_cond_signal(&count_ready); pthread_mutex_unlock(&count_lock); pthread_exit(NULL); } int main() { pthread_t tid1,tid2,tid3; count=0; pthread_mutex_init(&count_lock, NULL); pthread_cond_init(&count_ready, NULL); pthread_create(&tid1, NULL, decrement_count, NULL); sleep(3); pthread_create(&tid3, NULL, decrement_count, NULL); sleep(3); pthread_create(&tid2, NULL, increment_count, NULL); /*等待decrement退出*/ pthread_join(tid2, NULL); printf("decrement quitn"); pthread_join(tid3, NULL); pthread_join(tid1, NULL); return 0; }
decrement:waiting 1482491648 decrement:waiting 1474098944 increment:running increment:count = 1 decrement:count = 1,1482491648 decrement quit
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算