操作系统-进程与线程(同步互斥典型模型-生产者,消费者模型、吸烟者问题)


王道计算机操作系统学习笔记心得

1.生产者,消费者模型

生产者消费者模型概述:

有一个缓冲区,生产者线程向缓冲区中填入数据,消费者线程从缓冲区中读取出一个数据。

需要注意

  1. 只有当缓冲区有数据时,消费者线程才可以取出缓冲区中的数据。否则阻塞等待
  2. 只有缓冲区有空间时,生产者线程才可以向缓冲区生产数据。否则阻塞等待
  3. 缓冲区属于临界资源,线程访问时需要互斥访问。

根据上面三种注意事项和信号量的PV操作可以分析出:

  • 缓冲区还有数据V======>P消费者线程消费

  • 生产者线程(进程)生产P<========V缓冲区还有控件

  • 临界资源互斥访问,可以使用互斥信号量来实现。或者加锁实现。

伪代码如下:

semaphore mutex = 1;
//互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n;
//同步信号量,表示空闲缓冲区的数量,n代表缓冲区的大小
semaphore full = 0;
//同步信号量,表示产品的数量,也即非空缓冲区的数量

//生产者
producer () {
	while(true){
		生产一个缓冲区数据;
		P(empyt)//空闲信号量-1
		
		//互斥访问临界区
		P(mutex)
		把产品放入缓冲区;
		V(mutex)
		
		V(full)//代表数据的信号量+1
	}
}

//消费者
consumer () {
	while(true){
		P(full);//数据信号量因为取出了要-1
		
		//互斥访问临界区
		P(mutex)
		从缓冲区取出一个数据;
		V(mutex)
		
		V(empty)//缓冲区空闲增加,empyt信号量+1
		使用缓冲区数据;
	}
}

实例:Linux环境下C++实现环形生产者消费者模型,这里互斥操作使用互斥锁实现

#pragma once 

#include<iostream>
#include<vector>
#include<semaphore.h>
#include<pthread.h>

#define SIZE 32

template<class T>
class RingQueue
{
  private:
    std::vector<T>q;
    int Cap;

    int b_pos;//只有生产者线程会访问,单生产者不需要保护这个变量,下面也是如此
    int d_pos;

    sem_t block_Num;
    sem_t data_Num;
  private:
    void P(sem_t&s)
    {
      sem_wait(&s);
    }
    void V(sem_t&s)
    {
      sem_post(&s);
    }
  public:
    RingQueue(int cap=SIZE):Cap(cap),b_pos(0),d_pos(0)
    {
      q.resize(Cap);
      sem_init(&block_Num,0,Cap);//开始空间信号量为Cap
      sem_init(&data_Num,0,0);//开始数据信号量为0
    }
    
    void Push(const T&in)//生产数据
    {
      P(block_Num);//关心空间信号量,申请空间信号量,P操作使空间信号量-1
      q[b_pos]=in;
      V(data_Num);//生产者生产了一个数据,数据信号量,V操作使数据信号量+1
      b_pos++;
      b_pos%=Cap;
    }

    void Pop(T&out)
    {
      P(data_Num);//当数据信号量为0时,消费者线程挂起等待,所以一定是生产者线程先运行,消费者线程后运行
      out=q[d_pos];
      V(block_Num);//消费了数据,所以空间信号量V操作使空间信号量+1
      d_pos++;
      d_pos%=Cap;
    }

    ~RingQueue(){
      sem_destroy(&block_Num);
      sem_destroy(&data_Num);
    }
};

多生产者,多消费者模型

问题描述:
A生产者向缓冲区生产A数据,A数据只能被A消费者消费。
B生产者向缓冲区生产B数据,B数据只能被B消费者消费。

这四个线程(进程)公用一个缓冲区,通过信号量描述上述关系。

首先:分析同步互斥关系

  1. 缓冲区是临界资源,需要互斥访问。A,B生产者是互斥关系;A,B消费者是互斥关系。
  2. A生产者生产后,A消费者下可以消费,是同步关系
  3. 同理B生产者和B消费者也是同步关系

伪代码:

semaphore mutex = 1;
//实现互斥访问缓冲区
semaphore a = 0;
//缓冲区中有几个A资源
semaphore b = 0;
//缓冲区中有几个B资源
semaphore buff = 1;
//缓冲区大小

A(){
	while(true){
		生产A
		//申请缓冲区资源
		P(buff)
		
		//互斥访问临界资源
		P(mutex)
		将A放入缓冲区中
		V(mutex)
		
		V(a)//A资源多了一个,信号量a++
	}
}

B(){
	while(true){
		生者B
		P(buff)
		P(mutex)
		将B放入缓冲区中
		V(mutex)
		V(b)
	}
}

delA(){
	while(true){
		//看缓冲区中是否有自己需要的数据
		P(a)
		P(mutex)
		从缓冲区取出A
		V(mutex)
		//缓冲区多空出一个资源
		V(buff)
		处理A任务
	}
}

delB(){
	while(true){
		P(b)
		P(mutex)
		从缓冲区中取出B
		V(mutex)
		V(buff)
		处理B任务
	}
}

需要注意:
因为此题的缓冲区大小为1,所以其实可以不设置互斥信号量mutex也可以实现所有线程互斥访问临界资源。

如果缓冲区大小大于1,则就需要设置互斥信号量mutex实现互斥操作。

其次:在分析同步类问题时,需要抽象成事件的发生会导致那些事件发生来分析,不要从进程(线程)的具体操作会导致什么结果分析。

2. 吸烟者问题

问题描述:

  1. 假设一个系统有三个抽烟者进程和一个供应者进程。
  2. 每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。
  3. 三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。
  4. 供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)

首先分析同步互斥关系:

  1. 桌子可以看作缓冲区,进程(线程)访问需要互斥访问
  2. 供应者可以看作生产者线程,生产三种组合的数据。
    组合一:纸+胶水 组合二:烟草+胶水 组合三:烟草+纸
  3. 缓冲区是组合一,一号吸烟者运行,同步关系
  4. 同理三种组合就是三组同步关系,每一组同步关系都需要设置一个信号量
  5. 吸烟者完成后,发送消息给供应者。是同步关系

因为,缓冲区大小为1,所以不需要这是互斥变量来保证临界资源安全。

伪代码:

semaphore offer1 = 0;
//桌上组合一的数量
semaphore offer2 = 0;
//桌上组合二的数量
semaphore offer3 = 0;
//桌上组合三的数量
semaphore finish = 0;
//抽烟是否完成
int i=0;
//用来实现三个吸烟者轮流吸烟(i=(i+1)%3来实现轮流吸烟)

provider (){
	while (1){
		if(i==0){
			将组合一放桌上;
			V(offer1);//通知吸烟者,资源就绪
		}
		else if(i==1){
			将组合二放桌上;
			V(offer2);
		}
		else{
			将组合三放桌上;
			V(offer3);
		}
		i=(i+1%3;
		//等待吸烟者吸烟完毕后继续循环
		P(finish);
	}
}

smoker1 ({
	while(1{
		P(offer1)
		从桌上拿走组合一;
		卷烟;抽掉;
		V(finish)
	}
}

smoker2 ({
	while(1{
		P(offer2)
		从桌上拿走组合二;
		卷烟;抽掉;
		V(finish)
	}
}

smoker3 ({
	while(1{
		P(offer3)
		从桌上拿走组合三;
		卷烟;抽掉;
		V(finish)
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NUC_Dodamce

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

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

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

打赏作者

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

抵扣说明:

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

余额充值