条件变量也是Linux中的一种同步技术,不过其一般不单独使用而是要和互斥锁一起配合使用。
如果单独使用互斥量的话,很容易发生死锁,所以条件变量为次应运而生^_^ !
条件变量允许一个线程阻塞和等待另一个线程发送的信号,使用条件变量可以以原子的方式阻塞线程,直到满足某个条件为止,可以避免忙等。
条件变量通常和互斥锁一起使用,互斥量主要用来保证对临界区的互斥进入,而条件变量则用于线程的阻塞等待,互斥锁锁定进入临界区以后,若条件不满足,线程就转为等待状态,等待条件满足后再被唤醒执行,否则继续执行,执行完成后解锁。
条件变量的类型:pthread_cond_t
条件变量的相关函数:
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
该函数是条件变量的等待函数,等待条件为真时唤醒相应的等待线程。
该函数在执行的时候可看成是执行了两个过程:(1)阻塞调用线程;(2)对互斥量进行解锁操作。但是其内部实现其实是先解锁互斥量然后再调用某个函数进行阻塞等待。当其等待条件变为真时,其先进行互斥量的加锁操作之后执行相应的操作。
int pthread_cond_broadcast(pthread_cond_t *cond);
该函数的作用就是当条件cond变为真时,给等待该条件变为真的所有线程以广播的方式发送信号。之后所有等待该条件的线程都被唤醒。
int pthread_cond_signal(pthread_cond_t *cond);
该函数的作用就是当条件cond为真时,给等待该条件变为真的线程发送信号,但是最终只能有一个线程被唤醒去执行相应的程序。
条件变量的初始化和销毁函数:
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量的摧毁函数,当条件变量使用完成后摧毁该条件变量。
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
该函数是条件变量的初始化函数,cond参数是条件变量,attr是该条件的属性,该属性可以用属性的初始化函数初始化,也可以使用其默认属性。
pthread_cond_t cond = PTHREAD_COND_INITIALIZER该方式是将条件变量初始化为一个常值。
以上所提到的所有函数如果执行成功,返回0,失败就返回错误编号。
以下是一个使用互斥量和条件变量的进行同步的生产者消费者程序
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#define BUFFER_SIZE 6
#define MAX_SIZE 50
struct{
pthread_mutex_t mutex;
pthread_cond_t nofull;
pthread_cond_t noempty;
int buff[MAX_SIZE];
int nput; //存放数据的缓冲区下标
int nval; //存放的数据值
int count; //缓冲区值中的存放的数据个数
}shared = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER};
//生产者程序
void *produce(void *arg)
{
for( ; ; ){
pthread_mutex_lock(&shared.mutex); //加锁从此行至pthread_mutex_unlock形成临界区
if(shared.nval >= MAX_SIZE){ //若生产者产生的数据值>=最大值就解锁退出
pthread_mutex_unlock(&shared.mutex);
printf("hjhfrkfhrkjfhrkfr\n");
break;
}
if(shared.count < BUFFER_SIZE){ //若缓冲区中存放的数据个数小于缓冲区所能存放的个数
shared.buff[shared.nput] = shared.nval; //就继续在缓冲区中存入数据
shared.nput++; //改变缓冲区中存放的数据的下标
if(shared.nput >= BUFFER_SIZE){ //当缓冲区的数据的下标>=缓冲的大小时,又开始重新计数
shared.nput = 0;
}
shared.nval++; //生产者产生的数据值增加
shared.count++; //生产者产生的数据个数增加
pthread_cond_signal(&shared.noempty); //给消费者线程发送信号,告诉消费者线程缓冲区中有数据可读
}else{ //缓冲区空间不足,无法存入数据,使生产者线程处于阻塞状态
pthread_cond_wait(&shared.nofull, &shared.mutex); //阻塞生产者线程,等待消费者线程给其发送缓冲区不满的信号
}
pthread_mutex_unlock(&shared.mutex); //解锁互斥量
}
}
//消费者程序
void *consume(void *arg)
{
int j = 0; //缓冲区的计数器,用来读出缓冲区中的数据
//遍历输出读出的数据
for(int i = 0; i < MAX_SIZE; ){
pthread_mutex_lock(&shared.mutex); //加锁互斥量至pthread_mutex_unlock形成临界区
if(shared.count > 0){ //若缓冲区中的数据个数>0就将其中的数据读出
printf("value = %d\n", shared.buff[j]);
//sleep(1);
j++; //缓冲区的数据下标,后移读出下一个位置的数据
if(j >= BUFFER_SIZE){ //若该计数下标>=缓冲区的大小,就重新从下标0开始重新计数
j = 0;
}
shared.count--; //缓冲区中的元素个数减一
pthread_cond_signal(&shared.nofull); //给生产者线程发送信号,告诉生产者线程缓冲区不满,可以存入数据
i++; //计数器,用来计数该读出的数据的个数
}else{ //缓冲区中没有数据可以读出,就阻塞等待缓冲区中填入数据
pthread_cond_wait(&shared.noempty, &shared.mutex); //阻塞等待生产者线程发送缓冲区不空的信号
}
pthread_mutex_unlock(&shared.mutex); //解锁互斥锁
}
}
int main()
{
pthread_t ptid;
pthread_t ctid;
pthread_create(&ptid, NULL, produce, NULL); //创建生产者线程
pthread_create(&ctid, NULL, consume, NULL); //创建消费者线程
pthread_join(ptid, NULL); //终止生产者线程
pthread_join(ctid, NULL); //终止消费者线程
return 0;
}