C语言之简单使用互斥锁条件锁实现生产者消费者模型操作

Posted 你是小KS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言之简单使用互斥锁条件锁实现生产者消费者模型操作相关的知识,希望对你有一定的参考价值。

当前版本:eclipse c++MinGW-W64-builds-4.3.5widnwos

1. 声明

当前内容主要为使用C语言中的条件锁和互斥锁实现生产者消费者模型,主要参考百度百科

所以使用wait方式时必须要在加锁和解锁范围内,非常类似java中synchronize(lock)wait()的代码,执行等待必须为线程持有锁

2. 基本demo

#include <stdio.h>
#include <stdlib.h>
// 从unistd中导入sleep函数
#include<unistd.h>
// 导入需要使用的线程库
#include <pthread.h>
/**
 * 	@description 使用条件锁方式实现生产者消费者模型
 * 		1. 使用条件锁方式 wait
 * 		2. 使用互斥锁
 * 	@author hy
 * 	@createTime 2022-04-16
 */

void startThread(void *(*run)(void *), void *arg);

int constumerSleepTime, productorSleepTime = 1000;
int goodsNum = 0;	  // 定义当前螺丝钉数量
int goodsMaxNum = 10; // 定义最大螺丝钉数量
int goodsMinNum = 0;  // 定义最少螺丝钉数量
// 定义互斥锁标记,用于控制对goodsNum的修改和访问操作
pthread_mutex_t lock;

pthread_mutex_t productLock; // 定义生产者锁
pthread_mutex_t constumeLock; // 定义消费者锁

// 定义条件锁,表示为触发的条件
pthread_cond_t productConditionLock; // 触发生产的条件锁
pthread_cond_t constumeConditionLock; // 触发消费的条件锁

int getGoodsNum() 
	pthread_mutex_lock(&lock);
	int gn = goodsNum;
	pthread_mutex_unlock(&lock);
	return gn;


void setGoodsNum(int num) 
	pthread_mutex_lock(&lock);
	goodsNum = num;
	pthread_mutex_unlock(&lock);


// 生产者
void* productor(void* arg) 
	// 得到当前执行的线程的执行id
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	// 执行特定次打印就结束

	while (1) 
		//pthread_mutex_lock(&productLock);
		int num = getGoodsNum();

		if (num >= goodsMaxNum) 
			pthread_mutex_lock(&productLock);
			printf("%s 由于螺丝钉生产满了,停下了!\\n", thread_name);
			fflush(stdout);
			// 应该停止生产螺丝钉,等待消费者拧螺丝钉
			/*pthread_cond_signal(&constumeConditionLock);*/
			// 唤醒所有等待的消费者
			pthread_cond_broadcast(&constumeConditionLock);
			pthread_cond_wait(&productConditionLock, &productLock);
			pthread_mutex_unlock(&productLock);
		 else 
			// 否则就应该继续生产螺丝钉
			pthread_mutex_lock(&lock);
			goodsNum++;
			printf("%s 生产了 1 个螺丝钉! 总共生产有 %d 个螺丝钉 !\\n", thread_name, goodsNum);
			fflush(stdout);
			pthread_mutex_unlock(&lock);
			// 每次产出一个螺丝钉后休息1秒
			_sleep(productorSleepTime);
			// 唤醒一个消费者
			//pthread_cond_signal(&constumeConditionLock);
			pthread_cond_broadcast(&constumeConditionLock);
		
		//pthread_mutex_unlock(&productLock);

	

	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	pthread_exit("productor shutdown now !");
	return NULL;


// 消费者模型
void* constumer(void* arg) 

	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	

	while (1) 
		//pthread_mutex_lock(&constumeLock);
		int num = getGoodsNum();
		if (num <= goodsMinNum) 
			pthread_mutex_lock(&constumeLock);
			printf("%s 由于没有螺丝钉,停下了!\\n", thread_name);
			fflush(stdout);
			// 唤醒一个生产者
			//pthread_cond_signal(&productConditionLock);
			// 应该唤醒所有的生产者
			pthread_cond_broadcast(&productConditionLock);
			pthread_cond_wait(&constumeConditionLock, &constumeLock);
			pthread_mutex_unlock(&constumeLock);
		 else 
			// 如果有螺丝钉就继续拧
			pthread_mutex_lock(&lock);
			goodsNum--;
			printf("%s 拧了1个螺丝钉!还有 %d 个螺丝钉要拧!\\n", thread_name, goodsNum);
			fflush(stdout);
			pthread_mutex_unlock(&lock);
			_sleep(constumerSleepTime); // 拧完一个螺丝钉后可以休息1秒钟
			// 唤醒一个生产者
			pthread_cond_signal(&productConditionLock);

		
		/*pthread_mutex_unlock(&constumeLock);*/
	

	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	pthread_exit("constumer shutdown now!");
	return NULL;


int main(int argc, char **argv) 
	void *(*product1)(void *) = productor;
	void *(*product2)(void *) = productor;
	void *(*product3)(void *) = productor;
	void *(*constum1)(void *) = constumer;
	void *(*constum2)(void *) = constumer;
	char* arg1[2] =  "productor-1" ;
	void* args1 = arg1; // 使用携带正确参数的可以执行,否则认为不可执行

	char* arg2[2] =  "productor-2" ;
	void* args2 = arg2;
	char* arg3[2] =  "productor-3" ;
	void* args3 = arg3;
	char* arg4[2] =  "constumer-1" ;
	void* args4 = arg4;
	char* arg5[2] =  "constumer-2" ;
	void* args5 = arg5;
	// 初始化访问goodsNum的锁
	pthread_mutex_init(&lock, NULL);
	// 初始化消费者生产者的锁
	pthread_mutex_init(&constumeLock, NULL);
	pthread_mutex_init(&productLock, NULL);
	// 初始化消费者生产者的条件锁
	pthread_cond_init(&constumeConditionLock, NULL);
	pthread_cond_init(&productConditionLock, NULL);
	startThread(product1, args1);
	startThread(product2, args2);
	startThread(product3, args3);
	//startThread(constum1, args4);
	startThread(constum2, args5);

	printf("after create thread\\n");
	while (1) 
		//printf("main thread wait! shutdownNum = %d\\n",shutdownNum);
		//fflush(stdout);
		sleep(1);
	
	printf("main thread shutdown!\\n");
	// 销毁互斥锁
	pthread_mutex_destroy(&lock);
	pthread_mutex_destroy(&constumeLock);
	pthread_mutex_destroy(&productLock);
	// 销毁条件锁
	pthread_cond_destroy(&constumeConditionLock);
	pthread_cond_destroy(&productConditionLock);
	return 0;

// 表现为启动一个线程,给定一个函数和参数
void startThread(void *(*run)(void *), void *arg) 
	pthread_t th;
	const pthread_attr_t *attr = NULL;
	void *(*func)(void *) = run;
	int result = pthread_create(&th, attr, func, arg);
	if (result != 0) 
		// 表示无法创建该线程
		printf("can't create thread in current application!");
		exit(-1);
	


3.测试

只有消费者的时候:


此时所有的消费者都进入wait进行等待被唤醒

只有生产者的时候:


生产者的生产数量大于指定时都进入wait了,等待被唤醒

启用生产者和消费者时


如果消费者没有物品必须等待生产者生产,生产者满了必须等待消费者消费,测试成功!

4. 问题总结

1. 如果使用wait的时候一定要有锁,必定像java中的锁包含范围,就是在wait前后必须有lock和unlock

2.条件锁必须和互斥锁一起使用才会实现条件唤醒功能

3.pthread_cond_wait表示的就是等待该条件锁(只要有广播pthread_cond_broadcast和唤醒pthread_cond_signal都会唤醒该等待的线程)

4.如果发现写的代码没有问题,一定要检查锁是否初始化并且传递的锁对象是否为指针,如果使用错误可能导致多线程代码失效(加锁或者条件锁无效)

以上是关于C语言之简单使用互斥锁条件锁实现生产者消费者模型操作的主要内容,如果未能解决你的问题,请参考以下文章

Go语言编程:使用条件变量Cond和channel通道实现多个生产者和消费者模型

在无锁实现中没有互斥锁的条件变量

python并发编程之多进程:互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型

C++线程间的互斥和通信

C++线程间的互斥和通信

python并发编程之多线程守护系列互斥锁生产者消费者模型