生产者消费者问题实践

目录

第1关:生产者消费者问题实践

任务描述

相关知识

多线程相关的系统调用

使用锁控制进程互斥

利用信号量实现进程同步

编程要求

测试说明

答案: 

第2关:进程互斥和同步

任务描述

相关知识

多线程相关的系统调用

答案:


第1关:生产者消费者问题实践

任务描述

生产者—消费者之间设置一个具有n个缓存区的缓冲池,生

pthread_t th //th是要等待结束的线程的标识
void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。
调用实例:pthread_join(thrd1, NULL);

产者进程将他所生产的产品放入一个缓冲区;消费者进程可以从一个缓冲区中取走产品去消费。不允许消费者进程到一个空缓冲去取产品。不允许生产者进程向一个已装满产品且尚未取走的缓冲区投放产品。

使用多线程实现生产者和消费者问题模型,使用锁和信号量控制线程之间的同步。

相关知识

为了完成本关任务,你需要掌握:1.多线程相关的系统调用,2.使用锁控制进程互斥,3.使用信号量控制进程同步

多线程相关的系统调用

include <pthread.h>

创建线程 pthread_create pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument);

线程创建函数包含四个变量,分别为:

1.一个线程变量名,被创建线程的标识

2. 线程的属性指针,缺省为NULL即可

3. 被创建线程的程序代码

4. 程序代码的参数

线程等待 pthread_join pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:

pthread_t th //th是要等待结束的线程的标识
void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。
调用实例:pthread_join(thrd1, NULL);
使用锁控制进程互斥

在主线程中初始化锁为解锁状态

pthread_mutex_t mutex;

pthread_mutex_init(&mutex, NULL);

访问对象时的加锁操作与解锁操作

加锁 pthread_mutex_lock(&mutex)

释放锁 pthread_mutex_unlock(&mutex)

利用信号量实现进程同步

先引入头文件 #include <semaphore.h>

初始化信号量: int sem_init(sem_t \*sem, int pshared, unsigned int value);

成功返回0,失败返回-1

参数

sem:指向信号量结构的一个指针

pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享 value:信号量的初始值

信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem);

成功返回0,失败返回-1

参数 sem:指向信号量的一个指针

信号量加1操作 int sem_post(sem_t *sem);

参数与返回同上 销毁信号量 int sem_destroy(sem_t *sem);

参数与返回同上

 

编程要求

请参考生产者线程的函数代码实现消费者线程的函数代码,将Consumer()函数补充完整。

测试说明

需要打印出生产者和消费者线程并发执行的情况,并满足题目所示的限制条件 预期输出结果类似下面这样。因为调度次序随机,执行结果可能和这个不同,但需要满足题目限制要求。

Produce one message:1
Consume one message:1
Produce one message:2
Consume one message:2
Produce one message:3
Consume one message:3
Produce one message:4
Consume one message:4
Produce one message:5
Consume one message:5
Produce one message:6
Produce one message:7
Consume one message:6
Consume one message:7
Produce one message:8
Produce one message:9
Consume one message:8
Produce one message:10
Consume one message:9
Consume one message:10
答案: 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

#define SIZE 10

int in = 0;
int out = 0;
int buffer[SIZE];
sem_t empty;
sem_t full;
pthread_mutex_t mutex;

void *Producer()
{
    int nextp = 0;
    int i = 0;
	for(; i < 10; ++i)
	{  
		int time = rand() % 10 + 1;
                                usleep(time*100000); 
                                sem_wait(&empty); 
		pthread_mutex_lock(&mutex);
		nextp = nextp + 1;
		buffer[in] = nextp;
		printf("Produce one message:%d\n", nextp);
		fflush(stdout);//printf后请一定调用这句刷新输出缓存
		in = (in + 1) % SIZE;
		pthread_mutex_unlock(&mutex);         //互斥锁解锁
		sem_post(&full);
	}
}

void *Consumer()
{
	//请补充消费者线程函数代码
    int nextc=0;
    int i=0;
    for (;i<10;++i){
        sem_wait(&full);  //等待缓冲区非空
        pthread_mutex_lock(&mutex);  //加锁
        nextc = buffer[out];
        printf("Consume one message:%d\n",nextc);
        fflush(stdout);
        out=(out+1)%SIZE;
        pthread_mutex_unlock(&mutex);  //解锁
        sem_post(&empty);  //增加一个空缓冲区
    }
}


int main()
{	
    sem_init(&empty, 0, 10);    //信号量初始化(最多容纳10条消息,容纳了10条生产者将不会生产消息)
	sem_init(&full, 0, 0);		
	pthread_mutex_init(&mutex, NULL);  //互斥锁初始化		
	pthread_t producid;	
	pthread_t consumid;		
	pthread_create(&producid, NULL, Producer, NULL);   //创建生产者线程	
	pthread_create(&consumid, NULL, Consumer, NULL);   //创建消费者线程	
	pthread_join(producid, NULL); 	
	pthread_join(consumid, NULL);   
	sem_destroy(&empty);         //信号量的销毁
	sem_destroy(&full);    
	pthread_mutex_destroy(&mutex);   //互斥锁的销毁
	return 0;
}

第2关:进程互斥和同步

任务描述

桌上有个能盛的下五个水果的空盘子。爸爸不停的向盘中放苹果或桔子,儿子不停的从盘中取出桔子享用,女儿不停的从盘中取出苹果享用。规定三人不能同时从盘中取放水果。试用信号量实现爸爸、儿子和女儿这三个进程之间的同步

相关知识

为了完成本关任务,你需要掌握:1.多线程相关的系统调用,2.使用锁控制进程互斥,3.使用信号量控制进程同步

多线程相关的系统调用

include <pthread.h>

创建线程 pthread_create pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument); 线程创建函数包含四个变量,分别为:

1.一个线程变量名,被创建线程的标识

2. 线程的属性指针,缺省为NULL即可

3. 被创建线程的程序代码

4. 程序代码的参数

线程等待 pthread_join pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:

pthread_t th //th是要等待结束的线程的标识
void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。
调用实例:pthread_join(thrd1, NULL);
#### 使用锁控制进程互斥
在主线程中初始化锁为解锁状态 
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
访问对象时的加锁操作与解锁操作 
加锁 pthread_mutex_lock(&mutex)
释放锁 pthread_mutex_unlock(&mutex)
#### 利用信号量实现进程同步
先引入头文件#include <semaphore.h>
初始化信号量: int sem_init(sem_t *sem, int pshared, unsigned int value); 
成功返回0,失败返回-1
参数
sem:指向信号量结构的一个指针
pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
value:信号量的初始值
信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem); 
成功返回0,失败返回-1
参数
sem:指向信号量的一个指针
信号量加1操作 int sem_post(sem_t *sem); 
参数与返回同上
销毁信号量 int sem_destroy(sem_t *sem); 
参数与返回同上
### 编程要求
请参考爸爸线程的函数代码实现儿子和女儿线程的函数代码,将Son()函数和Daughter()函数补充完整。
### 测试说明
需要打印出爸爸、儿子和女儿线程并发执行的情况,并满足题目所示的限制条件。
爸爸、儿子和女儿的程序流程如下:
semaphore empty = 5,orange = 0,apple = 0, mutex = 1;
Dad() {
  while(1) {
    wait(empty);
    wait(mutex);
    将水果放入盘中;
    signal(mutex);
    if(放入的是桔子) signal(orange);
    else signal(apple);
  }
}
Son() {
  while(1) {
    wait(orange);
    wait(mutex);
    从盘中取出一个桔子;
    signal(mutex);
    signal(empty);
    享用桔子;
  }
}
Daughter() {
  while(1) {
    wait(apple);
    wait(mutex);
    从盘中取出一个苹果;
    signal(mutex);
    signal(empty);
    享用苹果;
  }
}
输出结果参考如下:
爸爸放入了一个苹果
女儿取了一个苹果
爸爸放入了一个桔子
儿子取了一个桔子
爸爸放入了一个苹果
女儿取了一个苹果
爸爸放入了一个桔子
儿子取了一个桔子
爸爸放入了一个苹果
女儿取了一个苹果
爸爸放入了一个桔子
儿子取了一个桔子
爸爸放入了一个苹果
女儿取了一个苹果
爸爸放入了一个桔子
儿子取了一个桔子
爸爸放入了一个苹果
女儿取了一个苹果
爸爸放入了一个桔子
儿子取了一个桔子
---
开始你的任务吧,祝你成功!
答案:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>


sem_t apple;
sem_t orange;
sem_t empty;
pthread_mutex_t mutex;

void *Dad()
{
	int nextp = 0;
	int i = 0;
    for(i = 0; i < 10; ++i)
	{
		int time = rand() % 10 + 1;          //随机使程序睡眠0点几秒
		usleep(time*100000);        
		sem_wait(&empty); 
		pthread_mutex_lock(&mutex);
		if(nextp == 0)
		{
			printf("爸爸放入了一个苹果\n");
		}
		else
		{
			printf("爸爸放入了一个桔子\n");
		}
        fflush(stdout);
		
		pthread_mutex_unlock(&mutex);         //互斥锁解锁
		
		if(nextp == 0)
		{
			sem_post(&apple);
		}
		else
		{
			sem_post(&orange);
		} 
		nextp = 1 - nextp; 
	}
}

void *Daughter()
{
	while(1)
	{
		int time = rand() % 10 + 1;          //随机使程序睡眠0点几秒
		usleep(time * 100000);        
		sem_wait(&apple); 
		pthread_mutex_lock(&mutex);
		printf("女儿取了一个苹果\n") ;
        fflush(stdout);
		pthread_mutex_unlock(&mutex);         //互斥锁解锁
		sem_post(&empty);
	}
}

void *Son()
{
	//请添加儿子线程的函数代码
    while (1) {
		int time=rand()%10+1;  //随机使程序睡眠0点几秒
		usleep(time*100000);
		sem_wait(&orange);
		pthread_mutex_lock(&mutex);
		printf("儿子取了一个桔子\n");
		fflush(stdout);
		pthread_mutex_unlock(&mutex);  //互斥锁解锁
		sem_post(&empty);
	}
}


int main()
{			
    sem_init(&empty, 0, 5);    //信号量初始化
	sem_init(&orange, 0, 0);
	sem_init(&apple, 0, 0);		
	pthread_mutex_init(&mutex, NULL);  //互斥锁初始化		
	pthread_t dadid;	
	pthread_t daughterid;
	pthread_t sonid;		
	pthread_create(&dadid, NULL, Dad, NULL);   //创建爸爸线程	
	pthread_create(&daughterid, NULL, Daughter, NULL);   //创建女儿线程
	pthread_create(&sonid, NULL, Son, NULL);   //创建儿子线程		
	pthread_join(daughterid, NULL);
	pthread_join(sonid, NULL);   
	
	sem_destroy(&empty);         //信号量的销毁
	sem_destroy(&apple);
	sem_destroy(&orange);    

	

	pthread_mutex_destroy(&mutex);   //互斥锁的销毁

	

	return 0;
}
### 回答1: educoder生产者消费者问题实践是一种经典的多线程编程问题,旨在通过生产者和消费者之间的协作来实现数据的安全共享。在这个问题中,生产者负责生产数据并将其放入缓冲区中,而消费者则负责从缓冲区中取出数据并进行消费。为了保证数据的安全共享,需要使用同步机制来避免生产者和消费者之间的竞争条件和死锁等问题。通过实践这个问题,可以加深对多线 ### 回答2: 生产者消费者(Producer-Consumer)问题是计算机领域中的经典问题,主要围绕着线程同步问题展开。在educoder上有一道题目生产者消费者问题实践题目,我们需要通过编程来实现一个基于生产者消费者模型的程序。 在本题目中,我们需要编写一个程序,该程序运行包含两个线程,一个是生产者线程,一个是消费者线程,他们需要同时运行。生产者线程会不断的生产商品并添加到一个“篮子”中,消费者线程则会从这个“篮子”中取出商品进行消费。如果篮子中已经有了商品,那么生产者就需要等待,直到消费者消费了一些商品,并释放了“篮子”空间。 在解决这个问题的时候,我们需要用到共享资源的概念。在本题目中,我们将“篮子”的空间看做是一个共享资源,需要互斥地被生产者和消费者访问。因此,我们需要使用一些操作系统提供的同步机制,例如锁,信号量等来保证线程之间的同步与互斥。 具体实现的方法如下:首先,我们需要定义一个“篮子”类,其内部需要定义一个商品队列,以及必要的状态锁,信号量等。然后,我们需要在主函数中创建生产者和消费者线程,并将“篮子”对象作为参数传递给这两个线程。生产者线程会在一个 while 循环中不断地生产商品,将商品添加到“篮子”队列中,并通知消费者线程有新的商品可以消费;消费者线程则会在一个 while 循环中不断地从“篮子”队列中取出商品进行消费,如果队列为空,则会等待生产者线程通知有新的商品到来。 总的来说,这个实践题目帮助我们更好的理解了生产者消费者模型,并加深了我们对线程同步机制的理解和运用技能。 ### 回答3: 生产者消费者问题是一种经典的并发编程问题,在并发编程中经常遇到。该问题的主要目的是协调“生产者”和“消费者”的操作,使得它们能够有序地进行操作,不会发生死锁或饥饿等问题。在Educoder生产者消费者问题实践中,通过理论学习和实践操作,我对该问题有了更深入的理解。 首先,我们知道,生产者负责生产物品并将其放置在缓冲区,消费者则从缓冲区中取出物品进行处理。因此,在实践中,我们需要考虑如何保证缓冲区的线程安全性。在Educoder的实践中,我通过Semaphore信号量进行了操作。Semaphore是一种计数信号量,用于保护共享资源,可以指定一个计数值,其中生产者可以增加计数值,消费者可以减少计数值,当计数值为0时,消费者必须等待,直到有生产者增加计数值。 其次,在实践中,我们还需要考虑如何避免死锁和饥饿问题。由于生产者和消费者的操作互相依赖,在多线程操作中容易产生死锁的情况,因此我们需要采取相应的策略来避免死锁情况的发生。在Educoder的实践中,我采用了先生产后消费的策略,确保在生产者将物品放入缓冲区之后,消费者才能进行取出和处理。此外,我还采用了公平锁来避免饥饿问题的发生,保证了每个线程都有机会参与执行。 总之,在Educoder生产者消费者问题实践中,我学习了并发编程的基础知识,并通过实践操作加深了对Semaphore的理解。通过理论结合实践,我对生产者消费者问题有了更深入的认识,同时也掌握了更多的编程技巧和策略,为以后的并发编程操作奠定了更加坚实的基础。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星与星熙.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值