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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值