条件变量的概述
条件变量不是锁,但它可以和互斥锁配合使用,互斥锁有两种状态:锁定和非锁定,但是互斥锁无法保证线程执行的先后顺序,这时就需要条件变量。
条件变量相关的函数
初始化一个条件变量
pthread_cond_init(pthread_cond_t * restrict cond,const pthread_condattr_t * restrict attr);
销毁一个条件变量
pthread_cond_destroy(pthread_cond_t * cond);
阻塞等待一个条件变量
pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t * restrict mutex);
在同一个线程里,互斥锁已经上锁,使用pthread_cond_wait(),会瞬间将已经上锁的mutex解锁,并阻塞线程,
当被其他线程唤醒时,该函数解除阻塞,对互斥锁加锁
限时等待一个条件变量
pthread_cond_timedwait(pthread_cond_t * restrict cond,pthread_mutex_t * restrict mutex
,const struct timespec * restrict abstime);
唤醒至少一个阻塞在条件变量上的线程
pthread_cond_signal(pthread_cond_t* cond);
唤醒全部阻塞在条件变量上的线程
pthread_cond_broadcast(pthread_cond_t * cond);
练习
使用条件变量实现生产者,消费者模型
cond.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
//定义一个链表节点类型
typedef struct node
{
int data ;
struct node * next;
}NODE;
pthread_mutex_t mutex;
pthread_cond_t cond;
NODE * head=NULL;
void *producer(void * arc)
{
while(1)
{
//创建链表节点
NODE* pnew=(NODE *)malloc(sizeof(NODE));
//初始化节点
pnew->data=rand()%1000;//0-999
printf("producter data is %d\n",pnew->data);
pthread_mutex_lock(&mutex);
pnew->next=head;
head=pnew;
pthread_mutex_unlock(&mutex);
//唤醒等待条件的线程
pthread_cond_signal(&cond);
sleep(rand()%3);//0-2
}
return 0;
}
void *customer(void * arc)
{
while(1)
{
pthread_mutex_lock(&mutex);
if(head==NULL)//如果共享区域没有数据,则解锁并等待唤醒
{
pthread_cond_wait(&cond,&mutex);
}
NODE* pdel=NULL;
pdel=head;
head=pdel->next;
pthread_mutex_unlock(&mutex);
printf("customer data is %d\n",pdel->data);
free(pdel);//释放被删除的节点内存
pdel=NULL;//将删除的节点指针指向NULL,防止野指针
sleep(rand()%3);
}
return 0;
}
int main()
{
pthread_t id1,id2;
//init mutex
pthread_mutex_init(&mutex,NULL);
//init condition
pthread_cond_init(&cond,NULL);
pthread_create(&id1,NULL,producer,NULL);
pthread_create(&id2,NULL,customer,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
}
条件变量的优点:
对比互斥锁,条件变量可以减少竞争。
如果直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果链表中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。