二十二、线程同步经典问题模型

本文详细探讨了线程同步的三个经典问题模型:生产者消费者模型,读者写者问题,以及哲学家就餐问题。生产者消费者模型通过互斥锁和信号量确保生产者和消费者对缓冲区的正确访问。读者写者问题重点在于允许多个读者同时访问数据,但限制写者。哲学家就餐问题则涉及避免死锁,提出三种解决方案,包括限制同时进餐的哲学家人数、一次性分配两根筷子和按编号顺序取筷子。文章通过代码示例阐述了解决这些问题的策略和实现方式。
摘要由CSDN通过智能技术生成

一、生产者消费者模型

(一)问题描述

【1. 问题描述:】

生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。 如下图所示:
在这里插入图片描述

【2. 关键点:】

  • 消费者和生产者必须互斥的使用缓冲区。
  • 缓冲区空时,消费者不能读取数据。
  • 缓冲区满时,生产者不能添加数据。
  • 消费者和生产者是一种同步关系,即:在生产者生产时,消费者不能消费;消费者消费时,生产者不能生产;只有生产者生产之后,消费者才能消费。

(二)问题分析

首先我们要明确一个点:我们需要循环从缓冲区放/取数据,缓冲区类似一个循环数组,即:

  • 生产者往数组中放数据,(i+1)%N的放,这样数组不会出现溢出情况。
  • 消费者从数组中取数据,(j+1)%N的取,这样不会出现数组越界。

我们假设缓冲区现在大小为5,如果我们不进行取余运算,那么生产者取数据时,j=6就会越界,但此时缓冲区中还有数据在1号为放着,所以应该取余6%5=1,到1号位去取数据。

其次,我们根据关键点确定我们需要控制三件事情:

  • 缓冲区互斥访问,我们可以用互斥锁,信号量来控制这两个线程对临界区的访问,因为只有1块临界资源,所以初始值为1。
  • 缓冲区所有格子放满了时,生产者不能生产。我们可以记录当前缓冲区没放数据的格子数,即空格子数,那么每次生产者往缓冲区放数据时空格子数量-1消费者取数据时,数量+1当空格子数==0时, 生产者-1会阻塞。故我们可以用信号量来表示缓冲区为空状态的控制,初始值为N,还没有往里面放数据,生产P(-1),消费(V+1),当P(-1)操作阻塞,表示缓冲区的所有格子都放满了。
  • 缓冲区中没有数据时,消费者不能消费,同理也用信号量来控制,我们记录当前放数据的格子的个数,初始值为0, 表示此时没有数据,生产者生产+1,消费者消费-1,当==0时,消费者阻塞。

假设我们定义三个控制变量分别为:mutex缓冲区信号量;empty缓冲区空格子数;full缓冲区填充数据的格子数。缓冲区用数组表示,大小为5,假设现在已经放了两个数据,那么有下图:

在这里插入图片描述
最后我们构思生产者消费者的整体流程,都是循环的读,写数据的,buff数组为临界资源。

生产者

  • P(empty) 操作,获取空格子数,此时empty-1,P(mutex)获取临界资源访问权,如果成功mutex=0,失败表示消费者正在读,阻塞mutex=-1。
  • 给buff[i]放入随机数,i=(i+1)%N,下一个放数据的位置。
  • V(full),full+1表示有数据格子数增加,V(mutex) 释放临界资源

消费者

  • P(full) 操作,读取数据,此时full-1,表示有数据格子数减一;P(mutex) 获取临界资源访问权,mutex=0。
  • 从buff[j]中取数据,j=(j+1)%N
  • V(empty),表示空格子数加一,V(mutex) 释放临界资源

(三)代码实现

我们开辟2个生产者,2个消费者,对缓冲区大小为5的buff进行写数据,取数据。

# include<stdio.h>
# include<assert.h>
# include<unistd.h>
# include<stdlib.h>
# include<pthread.h>
# include<semaphore.h>
# include<string.h>

int buff[5]={
   0};//缓冲区
sem_t mutex;//控制缓冲区
sem_t empty;//控制空格子数
sem_t full;//控制有数据格子

int i=0;
int j=0;


void* producer(void* argc)
{
   
    while(1)
    {
   
        int index=(int)(argc);
        srand(time(NULL));//生成随机数字
        int data=rand()%11;
        sem_wait(&empty);
        sem_wait(&mutex);
        buff[i]=data;
        printf("生产者线程%d往buff%d下标添加数字%d\n",index,i,data);
        i=(i+1)%5;
        sem_post(&full);
        sem_post(&mutex);
        sleep(1);
    }
}
void* consumer(void* argc)
{
   
    while(1)
    {
   
        int index=(int)(argc);
        srand(time(NULL));
        int data=rand()%11;
        sem_wait(&full);
        sem_wait(&mutex);
        printf("消费者线程%d从buff第%d取出数字%d\n",index,j,buff[j]);
        buff[j]=0;
        j=(j+1)%5;
        sem_post(&empty);
        sem_post(&mutex);
        sleep(1);
    }
}


int main()
{
   
    pthread_t id[4];
    sem_init(&mutex,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值