线程同步——条件变量

1 条件变量

使用读写锁、互斥锁可以阻塞线程,使对临界代码的操作变为原子操作。其临界区代码只有两种状态,锁定和非锁定。而如果需要外部的条件判断之后才执行某些临界区代码,比如经典的生产者-消费者模型,生产者和消费者对资源的使用时互斥的,但是消费者对资源的使用还有一个前提就是资源不为空,但是这个判断互斥锁没法实现。

因此需要使用条件变量,在消费者进入临界区并被告知当前无可用资源的时候,阻塞消费者线程;当生产者生产了可供消费者使用的资源之后,提示消费者可以使用资源(唤醒阻塞的消费者线程)。比如下面的代码:

        链表Node *head=NULL
		while(head){
			//想让代码在这个位置阻塞
			//等待链表中有了节点之后再继续向下运行
			//使用了后面要用的条件变量 – 阻塞线程
		}
		//链表不为空的处理代码

1.1 条件变量是锁吗?

条件变量不是锁,但是条件变量可以阻塞线程。

使用条件变量和互斥锁,其中:

互斥锁:保护一段共享区域;

条件变量:引起阻塞。

下面是经典的生产者-消费者模型。

生产者和消费者模型:生产者生产商品,消费者消费商品,共享“商品”资源。
消费者:一直消费
//一直消费
while(1){
	//判断是不是有烧饼
    pthread_mutex_lock(&mutex);
    if(head==NULL){
	    //阻塞等待
	    pthread_cond_wait(&cond,&mutex);
    }
    //吃烧饼
    pthread_mutex_lock(&mutex);
	sleep(1);
}

生产者:
//一直生产
while(1){
	//创建一个节点
	pthread_mutex_lock(&mutex);
	//节点插入到链表
	pthread_mutex_unlock(&mutex);
	//通知阻塞的消费者解除阻塞
    pthread_cond_signal();
}

1.2 条件变量的两个动作

  1. 条件不满足时,阻塞线程;
  2. 条件满足时,通知阻塞的线程开始工作。

1.3 条件变量的类型

//类型名       变量名
pthread_cond_t cond

1.4 主要函数

(1) 初始化一个条件变量

pthread_cond_init(
	pthread_cond_t* restrict cond,//条件变量类型的变量
	const pthread_condattr_t* restrict attr//参数,默认NULL
);

(2) 销毁一个条件变量

pthread_cond_destroy(pthread_cond_t* cond);

(3) 阻塞等待一个条件变量

pthread_cond_wait(
	pthread_cond_t* restrict cond, //条件变量类型的变量
	pthread_mutex_t* restrict mutex//互斥锁类型的变量
);

使用条件变量阻塞线程:传入参数为条件变量类型的变量以及互斥锁类型的变量;

使用条件变量阻塞线程时:将已经上锁的mutex解锁;

条件变量通知解除阻塞之后:加锁,访问共享数据。

(4) 限时等待一个条件变量

pthread_cond_timedwait(
	pthread_cond_t* restrict cond,
	pthread_mutex_t* restrict mutex,
const struct timespec* restrict abstime
);

(5)唤醒至少一个阻塞在条件变量上的线程

pthread_cond_signal(pthread_cond_t* cond);

(6)唤醒全部阻塞在条件变量上的线程

pthread_cond_broadcast(pthread_cond_t* cond);

2. 代码示例(C语言)

//消费者,生产者模型 
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <windows.h>
//创建节点的结构
typedef struct node{
	int data;
	struct node* next;
}Node;

//定义一个指针,指向链表头部
Node* head=NULL; 

//线程同步需要互斥锁
pthread_mutex_t mutex;
//条件变量
pthread_cond_t cond; 

 //生产者 
void* producer(void* arg){
	while(1){
		//创建一个链表的节点
		Node* pnew=(Node*)malloc(sizeof(Node)); 
		//节点初始化
		pnew->data=rand()%1000;
		
		//使用互斥锁 
		pthread_mutex_lock(&mutex); 
		//指针域
		pnew->next=head;
		head=pnew;//节点被插入到链表头部
		printf("===== produce: %lu, %d\n",pthread_self(),pnew->data); 
		//解锁 
		pthread_mutex_unlock(&mutex); 
		//通知阻塞的消费者线程 ,解除阻塞 
		pthread_cond_signal(&cond); 
		sleep(rand()%3);
	} 
	
	return NULL;
}

void* customer(void* arg){
	while(1){
		//加锁 
		pthread_mutex_lock(&mutex); 
		if(head==NULL){
			//线程阻塞
			//该函数会对互斥锁解锁 
			pthread_cond_wait(&cond,&mutex); 
			//解除阻塞之后,执行加锁操作 
		}
		//链表不为空,删除头节点 
		Node* pdel=head;
		head=head->next;
		printf("===== customer: %lu, %d\n",pthread_self(),pdel->data); 
		free(pdel);
		//解锁 
		pthread_mutex_unlock(&mutex); 
	}
	
	return NULL;
}

int main(void){
	pthread_t p1, p2;
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);
	//创建生产者线程
	pthread_create(&p1,NULL,producer,NULL); 
	pthread_create(&p1,NULL,customer,NULL); 
	
	
	//阻塞回收
	pthread_join(p1,NULL);
	pthread_join(p2,NULL);
	
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
	
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值