pthread_cond_signal pthread_cond_wait

来源:互联网 发布:空间备案还是域名备案 编辑:程序博客网 时间:2024/04/28 22:24

pthread_cond_signal

pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回

但使用pthread_cond_signal不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。另外,互斥量的作用一般是用于对某个资源进行互斥性的存取,很多时候是用来保证操作是一个原子性的操作,是不可中断的。

但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程. 

此外,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.pthread_cond_signal()也可能唤醒多个线程,而如果你同时只允许一个线程访问的话 强烈推荐对pthread_cond_wait() 使用while循环来做条件判断.

用法:

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放中间,但从编程规则上说,其他两种都可以


pthread_cond_wait() , 多线程的条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()pthread_cond_broadcast来唤醒它 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex

1. 创建和注销

 
条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,如下:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER
动态方式调用pthread_cond_init()函数,API定义如下:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。
注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:
int pthread_cond_destroy(pthread_cond_t *cond)
2. 等待和激发
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。

 以下就是一个来自MAN的示例

Consider two shared variables x and y, protected by the mutex mut, and a condition vari-

       able cond that is to be signaled whenever x becomes greater than y.
              int x,y;
              pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
              pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
       Waiting until x is greater than y is performed as follows:
              pthread_mutex_lock(&mut);
              while (x <= y) {
                      pthread_cond_wait(&cond, &mut);
              }
              /* operate on x and y */
              pthread_mutex_unlock(&mut);
       Modifications on x and y that may cause x to become greater than y should signal the con-
       dition if needed:
              pthread_mutex_lock(&mut);
              /* modify x and y */
              if (x > y) pthread_cond_broadcast(&cond);
              pthread_mutex_unlock(&mut);


pthread_cond_signal函数与条件变量的典型应用就是用来实现producer/consumer模型。

#include <stdio.h>#include <unistd.h>#include <pthread.h>#include <stdlib.h> #define BUFFER_SIZE 8 struct Products{int buffer[BUFFER_SIZE];/*保证存取操作的原子性 互斥性*/pthread_mutex_t locker;/*是否可读*/           pthread_cond_t notEmpty;/*是否可写*/  pthread_cond_t notFull;int posReadFrom;int posWriteTo;}; int BufferIsFull(struct Products* products)// 判断缓冲区是否装满{if ((products->posWriteTo + 1) % BUFFER_SIZE == products->posReadFrom){return (1);}return (0);} int BufferIsEmpty(struct Products* products)//判断缓冲区是否空{if (products->posWriteTo == products->posReadFrom){return (1);}return (0);} /*制造产品*/ void Produce(struct Products* products, int item){/*原子操作*/pthread_mutex_lock(&products->locker);/*无空间可写入*/while (BufferIsFull(products)){pthread_cond_wait(&products->notFull, &products->locker);}  /*写入数据*/products->buffer[products->posWriteTo] = item;products->posWriteTo++; if (products->posWriteTo >= BUFFER_SIZE)products->posWriteTo = 0;/*发信*/pthread_cond_signal(&products->notEmpty);/*解锁*/pthread_mutex_unlock(&products->locker);} /* 消费*/int Consume(struct Products* products){int item; pthread_mutex_lock(&products->locker);/*为空时持续等待,无数据可读*/while (BufferIsEmpty(products)){pthread_cond_wait(&products->notEmpty, &products->locker);} /*提取数据*/item = products->buffer[products->posReadFrom];products->posReadFrom++;/*如果到末尾,从头读取*/if (products->posReadFrom >= BUFFER_SIZE)products->posReadFrom = 0;pthread_cond_signal(&products->notFull); pthread_mutex_unlock(&products->locker); return item;} #define END_FLAG (-1) struct Products products; void* ProducerThread(void* data){int i;for(i = 0; i < 16; ++i){printf("producer: %d\n", i);Produce(&products, i);// 加入}Produce(&products, END_FLAG);return NULL;} void* ConsumerThread(void* data){int item; while(1){item = Consume(&products);if(END_FLAG == item)break;printf("consumer: %d\n", item);}return (NULL);} int main(int argc, char* argv[]){pthread_t producer;pthread_t consumer;int result; pthread_create(&producer, NULL, &ProducerThread, NULL);pthread_create(&consumer, NULL, &ConsumerThread, NULL); pthread_join(producer, (void *)&result);pthread_join(consumer, (void *)&result); exit(EXIT_SUCCESS);}
参考资料 http://baike.baidu.com/view/5725833.htm

             http://www.eefocus.com/andrew_dj/blog/12-08/282740_85e58.html