Linux:线程同步之条件变量(条件变量的概念、作用、使用方法、手写实现生产者-消费者模型)

1、条件变量

(1)What(什么是条件变量)

一种线程同步机制,在线程中以睡眠的方式等待某一条件的发生,当条件不成立时挂起线程,条件成立时唤醒线程

(2)Why(条件变量的作用)

通过条件的满足与否来控制线程的等待与运行,目的是实现对线程的等待与唤醒以保证多线程之间对共享资源的安全有序访问(即实现线程同步)

(3)How(如何使用条件变量实现线程同步)

条件变量通常和互斥锁结合使用

step01:定义条件变量

在Linux中,条件变量的本质是一个数据结构(不同的操作系统其本质可能有一点要求)

pthread_cond_t cond;

step02:初始化条件变量

本质是给条件变量赋初始值

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr=NULL);

参数cond:待初始化的条件变量
参数attr:条件变量的属性,不传代表使用默认参数
返回值:初始化成功返回0,初始化失败返回错误码

step03:阻塞线程

本质就是调用函数改变条件变量的状态,阻塞当前线程,直到条件变量被唤醒

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

参数cond:要等待的条件变量
参数mutex:与条件变量关联的互斥锁
返回值:成功返回0,失败返回错误码

#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

上述函数与 pthread_cond_wait 类似,但会在指定的超时时间timespec后自动唤醒

step04:唤醒线程

本质就是调用函数改变条件变量的状态,发送信号给条件变量,唤醒阻塞在当前条件变量下的一个线程

#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);

参数cond:要发送信号的条件变量
返回值:成功返回0,失败返回错误码

#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);

唤醒所有等待在该条件变量上的线程

step05:销毁条件变量

本质就是调用函数释放条件变量占用的内存空间

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
  • 参数cond:要销毁的条件变量
  • 返回值:成功返回0,失败返回错误码

说明:

在典型的使用场景中,线程在访问共享资源之前会先获取互斥锁,然后判断对应的条件变量是否满足,如果条件不满足,线程会调用条件变量的等待操作,并释放互斥锁进入阻塞状态;当其它线程修改了共享数据并满足条件时,它会通过条件变量的信号或广播操作来唤醒等待该条件的线程。被唤醒的线程重新获取互斥锁,检查条件是否满足,如果条件满足就继续执行临界区的操作。

(4)生产者-消费者模型

使用互斥锁和条件变量实现生产者-消费者模型。生产者-消费者模式是一种常见的并发编程模型,用于解决生产者和消费者之间的协作和同步问题。

A.What(什么是生产者-消费者模型)

一种常见的并发编程模型。模型包括三个重要元素:生产者、消费者和共享缓冲区。生产者不断生产数据,如果共享缓冲区已满,生产者会等待,直到缓冲区有空闲空间。消费者不断从缓冲区中获取数据,如果缓冲区为空,消费者会等待,直到有新的数据被生产者放入缓冲区。

B.Why(该模型的作用)

  • 解耦了生产者和消费者的操作,使它们可以独立地运行
  • 提高了系统的并发性和效率,生产者和消费者可以同时工作,不必相互等待

C.How(如何使用条件变量和互斥锁实现该模型)

下列示例中,生产者可以无限生产,消费者需要判断缓冲区是否有产品可以消费。

全局变量

pthread_cond_t cond; //条件变量,用于控制消费者线程
pthread_mutex_t mutex; //互斥锁
sturct Node * head = NULL; //缓冲区

生产者线程

void * produce(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		//TODO:向head中添加数据节点
		pthread_mutex_unlock(&mutex);
		pthread_cond_signal(&cond); //唤醒消费线程
	}
	return NULL;
}

消费者线程

void * consume(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		while(head == NULL) //不满足消费条件,所以阻塞
		{
			pthread_cond_wait(&cond, &mutex);
		}
		pthread_mutex_unlock(&mutex);
	}
	return NULL;
}

main函数

void main()
{
	pthread_mutex_init(&mutex);
	pthread_cond_init(&cond);
	pthread_t consumer[5], producer[5];
	for(int i = 0; i<5; ++i)
	{
		pthread_create(&consumer[i], NULL, consume, NULL);
		pthread_create(&producer[i], NULL, produce, NULL);
	}
	//TODO:释放工作
}
  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值