Linux多线程之条件变量(生产者和消费者模型)

条件变量

【条件变量本身不是锁!但是它也可以造成线程阻塞。通常与互斥锁配合使用。给多进程提供一个会和的场所(共享数据)】

主要应用函数

1. pthread_cond_init  		//初始化条件变量
2. pthread_cond_destroy		//销毁条件变量
3. pthread_cond_wait		//线程等待信号触发,如果没有信号触发,无限期等待下去。
4. pthread_cond_timedwait	//线程等待一定的时间,如果超时或有信号触发,线程唤醒。
5. pthread_cond_signal		//唤醒阻塞在该条件变量上的至少一个线程
6. pthread_cond_broadcast	//唤醒阻塞在该条件变量上的所有线程
【以上6个函数的返回值都是:成功:0;失败:错误号】
- pthread_cond_t 类型 用于定义 条件变量
- pthread_cond_t cond;

pthread_cond_init 函数

【初始化一个条件变量】
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr *restrict attr);
【参数2】attr为条件变量属性,通常为默认值,传NULL即可
也可以使用静态初始化的方法初始化条件变量
	pthread_cont_t cond = PTHREAD_COND_INITIALIZER

pthread_cond_destroy 函数

【销毁条件变量】
	int pthread_cond_destroy(pthread_cond_t *cond);

pthread_cond_wait 函数

【阻塞等待一个条件变量】
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
【函数作用】
1. 阻塞等待条件变量cond(参数1)满足
2. 释放已掌握的互斥锁(解锁互斥量)相当于 pthread_mutex_unlock(&mutex);
	[1,2两步为一个原子操作,意味着执行该函数之前首先得定义一个互斥量,并初始化,还要申请信号量]
3. 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);

pthread_cond_timedwait 函数

【限时等待一个条件变量】
	 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
【struct timespec 结构体】
struct timespec{
	time_t tv_sec;  //秒
	long tv_nsec;	//纳秒
};
【形参:abstime 绝对时间】
如:time(NULL) 返回的就是绝对时间。而alarm(1)是相对时间,相对当前时间定时1秒钟
	struct timespec t = {1, 0};
	pthread_cond_timedwait(&cond, &mutex, &t); //只能定时到1970年1月1日的00:00::01 秒(早已经过去)
【正确用法】
	time_t cur = time(NULL);  //获取当前时间
	struct timespec t;
	t.tv_sec = cur + 1; //定时1秒
	pthread_cond_timedwait(&cond, &mutex, &t);

pthread_cond_signal 函数

【唤醒阻塞在该条件变量上的至少一个线程】
	 int pthread_cond_signal(pthread_cond_t *cond);

pthread_cont_broadcast 函数

【唤醒阻塞在该条件变量上的所有线程】
	int pthread_cond_broadcast(pthread_cond_t *cond);
条件变量的优点
  • 相较于 mutex 而言,条件变量可以减少竞争
  • 如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
练习

用一个链表模拟生产者和消费者的临界区,实现消费者和生产者模型

#include 
#include 
#include 
#include 

//静态初始化的方法初始化 mutex 和 has_product
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;;

//定义一个链表实现生产者和消费者共享区
struct msg{
	struct msg *next;
	int num;
};
struct msg *head;
struct msg *node;
void *producer(void *arg)
{
	while(1)
	{
		node = (struct msg*)malloc(sizeof(struct msg));
		node->num = rand() % 100 + 1;
		pthread_mutex_lock(&mutex);
		node->next = head;
		head = node;  //头插法
		printf("Produce -- %d\n", node->num);
		pthread_mutex_unlock(&mutex);
		pthread_cond_signal(&has_product); //唤醒一个线程
		sleep(rand() % 5);
		
	}
	return NULL;
}
void *customer(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		// 此处不能用if语句 if(head == NULL):消费者线程可能有多个
		while(head == NULL)
		{
			pthread_cond_wait(&has_product, &mutex);
		}
		//消费链表的第一个节点
		node = head;
		head = node->next;
		printf("Costume -- %d\n", node->num);
		pthread_mutex_unlock(&mutex);
		free(node);  //不要忘记释放node
		sleep(rand() % 5);
	}
}
int main()
{
	pthread_t pid, cid;
	srand(time(NULL));
	//动态初始化
	//pthread_mutex_init(&mutex,NULL);
	//pthread_cond_init(&has_product, NULL);

	pthread_create(&pid, NULL, producer, NULL);
	pthread_create(&cid, NULL, customer, NULL);

	pthread_join(pid, NULL);
	pthread_join(cid, NULL);
	return 0;
}

【运行结果】因为在生产者函数中,每生产一个,就插入链表头,消费者每次都从链表头开始消费
Linux多线程之条件变量(生产者和消费者模型)_第1张图片

你可能感兴趣的:(Linux,C/C++,多线程,并发编程)