条件变量是线程可用的另一种同步机制。条件变量给多个线程提供了一个回合的场所。条件变量与互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生。条件本身是由互斥量保护的。线程在改变条件状态之前必须要首先锁住互斥量。其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定以后才能计算条件。
使用条件变量之前,需要定义该条件变量(全局变量),定义条件变量对象如下:
<span style="font-family:Microsoft YaHei;font-size:18px;">pthread_cond_t condtion;</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h> int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); //若成功,返回0;否则,返回错误编号</span>
第二个参数cond_attr是指向属性对象的指针,该属性对象定义要初始化的条件变量的特性,如果该指针为NULL,则使用默认的属性。
销毁:
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h> int pthread_cond_destory(pthread_cond_t *cond); //若成功,返回0;若失败,返回错误编号</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h> int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); //若成功,返回0;若失败,返回错误编号。</span>
(1)pthread_cond_signal()函数用来唤醒等待出现与条件变量cond关联的条件的第一个线程。如果cond上没有阻塞任何线程,则函数不起作用。如果cond阻塞了多个线程,则调度策略将确定要取消阻塞的线程。显然,在此函数中,隐含的释放了当前线程占有的信号量。
(2)pthread_cond_broadcast()函数用来唤醒等待出现与条件变量cond关联的所有线程。如果cond上没有阻塞任何线程,则此函数不起作用。
(1)pthread_cond_wait()函数用来阻塞等待某个条件变量。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h> int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);//若成功,返回0;若失败,返回错误编号。</span>
第一个参数cond是指向要等待的条件变量的指针。
第二个参数mutex是指向与条件变量cond关联的互斥锁的指针。
(2)pthread_cond_timedwait()函数将在指定的时间范围内等待条件变量。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h> int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr); //若成功,返回0;否则,返回错误编号。</span>
第一个参数cond是指向要等待的条件变量的指针。
第二个参数mutex是指向与条件变量cond关联的互斥锁的指针。
第三个参数abstime是等待过期时间的绝对时间,如果在此时间范围内取到该条件变量函数将返回。该时间为从1970-1-1:0:0:0以来的秒数,即为一个绝对时间。该数据结构声明如下:
<span style="font-family:Microsoft YaHei;font-size:18px;">struct timespec { long ts_sec; long ts_nsec; };</span>
以上两个函数都包含一个互斥锁,如果某线程因等待条件变量进入等待状态时,将隐含释放其申请的互斥锁,同样,在返回时,首先要申请到该互斥锁对象。
此程序用来处理生产消费问题,整个临时存储空间为2,即在任意时刻,最多能够有2个产品存放在临时空间,如果已经有2个产品存放在临时空间,将阻塞生产线程。同理,如果临时空间没有产品,显示需要阻塞消费线程。此程序中主要实现了生产和消费两个线程的同步。
在此程序中,使用了互斥锁对象以及条件变量两种线程间通信机制。互斥锁与条件变量协同工作,操作步骤如下:
(1)锁定互斥锁。
(2)测试条件是否满足。
(3)如果满足,执行操作,完成后解锁互斥锁。
(4)如果第2步的条件不满足,使用条件变量机制等待,当另一个线程使此条件满足时,执行第3步。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 2 //时间空间大小 struct prodcons //条件信息结构体 { int buffer[BUFFER_SIZE]; //生产产品值 pthread_mutex_t lock; //互斥锁 int readpos, writepos; //读写位置 pthread_cond_t notempty; //条件变量,表非空 pthread_cond_t notfull; //条件变量,表非满 }; void init(struct prodcons *prod) //初始化 { pthread_mutex_init(&prod->lock, NULL);//初始化互斥锁 pthread_cond_init(&prod->notempty, NULL);//初始化条件变量 pthread_cond_init(&prod->notfull, NULL);//初始化条件变量 prod->readpos = 0; //初始化读操作位置 prod->writepos = 0; //初始化写操作位置 } void put(struct prodcons *prod, int data) //输入产品子函数 { pthread_mutex_lock(&prod->lock); //锁定互斥量 while((prod->writepos + 1)%BUFFER_SIZE == prod->readpos) { //测试空间是否已满 printf("producer wait for not full\n"); pthread_cond_wait(&prod->notfull, &prod->lock);//等待有空间可写 } prod->buffer[prod->writepos] = data; //写数据 prod->writepos++; //写位置数加1 if(prod->writepos >= BUFFER_SIZE) //如果写到尾部,返回 prod->writepos = 0; pthread_cond_signal(&prod->notempty); //发送有数据信号 pthread_mutex_unlock(&prod->lock); //解锁 } int get(struct prodcons *prod) { int data; pthread_mutex_lock(&prod->lock); //锁上互斥锁 while(prod->writepos == prod->readpos) { //测试是否有数据 printf("consumer wait for not empty\n"); pthread_cond_wait(&prod->notempty, &prod->lock); } //如果为空,等待 data = prod->buffer[prod->readpos]; //读数据 prod->readpos++; //读指针加1 if(prod->readpos >= BUFFER_SIZE) //如果读到尾部,返回 prod->readpos = 0; pthread_cond_signal(&prod->notfull);//发有空间空闲信号 pthread_mutex_unlock(&prod->lock); //解锁 return data; } #define OVER (-1) struct prodcons buffer; void * producer(void *data) //生产者 { int n; for(n = 1; n <= 5; n++) //生产前5个产品 { printf("producer sleep 1 second .....\n"); sleep(1); //每1秒生产一个产品 printf("put the %d producer \n", n); put(&buffer, n); } for(n = 6; n <= 10; n++) //生产后5个产品 { printf("producer sleep 3 second ......\n"); sleep(3); //每3秒生产一个产品 printf("put the %d product \n", n); put(&buffer, n); } put(&buffer, OVER); printf("producer stopped!\n"); return NULL; } void * consumer(void *data) //消费者 { int d=0; while(1) { printf("consumer sleep 2 second ......\n"); sleep(2); //每2秒消费一个产品 d = get(&buffer); printf("get the %d product\n", d); if(d == OVER) break; } printf("consumer stopped!\n"); return NULL; } int main(int argc, char *argv[]) { pthread_t th_a, th_b; void * retval; init(&buffer); pthread_create(&th_a, NULL, producer, 0);//创建生产线程 pthread_create(&th_b, NULL, consumer, 0);//创建消费线程 pthread_join(th_a, &retval); //等待生产线程结束 pthread_join(th_b, &retval); //等待消费线程结束 return 0; } </span>