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

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简易的C语言代码实现生产者消费者问题,附有注释: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> // 缓冲区大小 #define BUFFER_SIZE 10 // 缓冲区 int buffer[BUFFER_SIZE]; // 缓冲区中元素数量 int count = 0; // 互斥锁 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 条件变量,用于生产者和消费者之间的同步 pthread_cond_t full = PTHREAD_COND_INITIALIZER; pthread_cond_t empty = PTHREAD_COND_INITIALIZER; // 生产者线程函数 void *producer(void *arg) { int i = 0; while (i < 20) { // 生产20个元素 pthread_mutex_lock(&mutex); // 加 // 如果缓冲区已满,则等待 while (count == BUFFER_SIZE) { pthread_cond_wait(&empty, &mutex); } // 生产一个元素,放入缓冲区 buffer[count++] = i; printf("Producing %d...\n", i); // 发出条件信号,唤醒等待在full条件变量上的消费者线程 pthread_cond_signal(&full); pthread_mutex_unlock(&mutex); // 解 i++; } return NULL; } // 消费者线程函数 void *consumer(void *arg) { int i = 0; while (i < 20) { // 消费20个元素 pthread_mutex_lock(&mutex); // 加 // 如果缓冲区为空,则等待 while (count == 0) { pthread_cond_wait(&full, &mutex); } // 从缓冲区中取出一个元素进行消费 int item = buffer[--count]; printf("Consuming %d...\n", item); // 发出条件信号,唤醒等待在empty条件变量上的生产者线程 pthread_cond_signal(&empty); pthread_mutex_unlock(&mutex); // 解 i++; } return NULL; } int main() { pthread_t p, c; // 创建生产者和消费者线程 pthread_create(&p, NULL, producer, NULL); pthread_create(&c, NULL, consumer, NULL); // 等待线程结束 pthread_join(p, NULL); pthread_join(c, NULL); return 0; } ``` 该代码实现了一个简单生产者消费者模型使用互斥锁条件变量来实现线程之间的同步与互斥。生产者线程不断生产元素并将其放入缓冲区,而消费者线程不断从缓冲区中取出元素进行消费。当缓冲区已满时,生产者线程会等待,直到有消费者取出元素后才会继续生产;当缓冲区为空时,消费者线程会等待,直到有生产者生产元素后才会继续消费。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值