消费者生产者问题,哲学家问题

消费者和生产者问题

生产者消费者共享缓冲区,生产者向缓冲区中放数据,消费者从缓冲取中取数据,当缓冲区中被放满时,生产者进程就必须进入挂起状态,直到消费者从缓冲中取走数据时,生产者才能继续向缓冲区中存放数据,同样当缓冲取中没有数据时,消费者进程就必须进入挂起休眠状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。
生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。

分析:
  • 1、对于变量empty和full 是去控制缓冲区产品的数量,防止超出缓冲区的大小
    假如现在只生产产品,而不去消费产品,empty降为0,full会上涨到100,满足了while内的条件,会陷入死循环,反复打印 “ 缓冲区满”,
  • 2、还会去加入一个锁,防止一个线程运行的时间片到了,进入其他线程消费或者生产产品,而未记录,产生错误
  • 3、如果线程在该时间片复内完成,线程就结束了,如果未完成,系统会将该线程放入就绪队列,等待下制CPU再度调度运行它,而且是接着上次执行的状态接着执行,
  • 4、用goto 语句 去防止不断的刷新缓冲区空 或缓冲区满
多消费者和多生产者代码:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

#define N 100
#define true 1
#define producerNum 10
#define consumerNum 5
#define sleepTime 1

//对于线程函数即使不需要参数,也不能写成void 要写成void * a ,在线程创建函数处写NULL
//
typedef int semaphore;
typedef int item;

//
//该缓冲区类似于队列,in 和 out 分别代表位置,入队和出对
item buffer[N] = { 0 };
int in = 0;
int out = 0;
int proCount = 0;    //记录产品ID
//empty 和 full 去记录缓冲区内产品数量,
//使用极端例子,假设只生产不消费,empty不断减小,减为0 时 缓冲区满,停止写入数据

semaphore mutex = 1,empty = N, full = 0, proCmutex = 1;

//用锁防止线程运行到中途时间片到
void * producer(void * a)
{
    while(true)
    {
        while(mutex <= 0);
        
        while(empty <= 0)
        {
            printf("缓冲区已满!\n");
        }
        empty --;
        mutex--;
        proCount ++;
        buffer[in] = proCount;
        in = (in + 1) % N;
        printf("生产的产品ID %d,缓冲区位置 %d\n",proCount,in);
        mutex ++;
        full ++;
        sleep(1);
    }
}
void *consumer(void * b)
{
    while(true)
    {
        while(mutex <= 0);
        while(full <= 0)
        {
            printf("缓冲区为空\n");
        }
        full --;
        mutex --;
        int nextc = buffer[out];
        buffer[out] = 0;
        out = (out + 1) % N;
        printf("\t\t\t\t消费的产品ID %d,缓冲区位置为%d\n",nextc,out);
        mutex ++;
        empty ++;
        sleep(1);

    }
}
int main(void)
{
    pthread_t pthreadPool[producerNum + consumerNum];
    for(int i = 0;i< producerNum;i++)
    {
        pthread_t temp;
        if(pthread_create(&temp,NULL,producer,NULL) == -1)
        {
            printf("%d : create producer pthread failed\n",__LINE__);
            exit(1);
        }
        pthreadPool[i] = temp;
    }
    for(int i = 0;i < consumerNum;i++ )
    {
        pthread_t temp;
        if(pthread_create(&temp,NULL,consumer,NULL) == -1)
        {
            printf("%d : create consumer pthread failed\n",__LINE__);
            exit(2);
        }
    }
    void *result;
    //使用pthread_join结束线程是因为线程运行会产生碎片垃圾,系统无法自动回收
    for(int i = 0;i < producerNum + consumerNum ;i++)
    {
        if(pthread_join(pthreadPool[i],&result) == -1)
        {
            printf("failed to recollect\n");
            exit(3);
        }
    }
    return 0;

}
单消费者和单生产者代码:

和多消费者多生产者代码基本一致,在创建线程时不同

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<semaphore.h>

//如果线程在该时间片复内完成,线程就结束了,如果未完成,系统会将该线程放入就绪队列,等待下制CPU再度调度运行它,而且是接着上次执行的状态接着执行,
//用goto 语句 去防止不断的刷新缓冲区空 或缓冲区满

typedef  int semaphore;

typedef int item;

#define true 1
#define N 3
#define sleepTime 1

item buffer[N] = { 0 };
int in = 0;
int out = 0;
int proCount = 1;

semaphore mutex = 1,empty = N, full = 0,proCmutex = 1;
void * producer(void * a)
{
    while(true)
    {
        while(empty <= 0)
        {
            printf("缓冲区已满!\n");
            goto a;
        }
        empty --;
       while(mutex <= 0); 
        mutex --;
        proCount ++;
        buffer[in] = proCount;
        in = (in + 1) % N;
        
        printf("生产的产品ID %d,缓冲区位置 %d\n",proCount,in);
        
        mutex++;
        full ++;
        a:sleep(2);
    }
}
void * consumer(void * b)
{
    while(true)
    {
        while(mutex <= 0);
        while(full <= 0)
        {
            printf("缓冲区为空!\n");
            goto a;
        }
        full --;
        mutex --;
        int nextc = buffer[out];
        buffer[out] = 0;
        out = (out + 1) % N;
        
        printf("\t\t\t\t消费的产品ID %d,缓冲区位置为%d\n",nextc,out);
        mutex++;
        empty ++;
        a:sleep(sleepTime);
    }
}

int main(void)
{
    pthread_t proID,conID;

    if(pthread_create(&proID,NULL,producer,NULL) == -1)
    {
        
        printf("%d : create producer pthread failed \n",__LINE__);
        exit(1);
    }
    if(pthread_create(&conID,NULL,consumer,NULL) == -1)
    {
        printf("%d : create consumer pthread failed\n",__LINE__);
        exit(2);
    }
    void * result;
    if(pthread_join(proID,&result) == -1)
    {
        printf("%d : recollect producer failed\n",__LINE__);
        exit(3);
    }
    if(pthread_join(conID,&result) == -1)
    {
        printf("%d : recollect consumer failed\n",__LINE__);
        exit(4);
    }
    return 0;
}

哲学家问题

有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。
哲学家进餐问题是一个多线程运用的经典例子,涉及到线程同步/互斥,临界区访问问题以及死锁问题。

分析:
  • 1、原子操作: 指的是有多部操作组成的一个操作。如果该操作不能原子的进行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集
  • 2、现代操作系统中,一般都提供了原子操作来实现一些同步操作,所谓原子操作,也就是一个独立而不可分割的操作,在单核环境中,一般意义下原子操作中线程不会被切换,线程切换要么在原子操作之前,要么在原子操作之后。更广泛的意义下原子操作是指一系列必须整体完成的操作步骤,如果任何一步操作没有完成,那么所有完成的步骤,都必须回滚,这样就可以保证要么所有操作步骤都未完成,要么所有操作步骤都被完成
  • 3、该问题用了一条汇编指令执行了交换函数,防止交换失败
void xchg(int *x,int *y)
{
   __asm__("xchgl %0,%1":"=r" (*x) : "m" (*y));
}

  • 4、加入一把全局锁,是为了能让某一个线程得到想要的资源,尽可能满足这一个线程,但是一定要注意解锁位置
  • 5、p、v操作
sem_init(&r,0,4)
设置信号量的初始值为4,保证最多有4个线程并行,第5个线程会阻塞
p操作                                     v操作
s = s - 1                                 s = s + 1;
if(s >= 0)                                if(s > 0)
    线程继续执行                                线程继续执行
if(s < 0)                                 if(s <= 0)
    线程阻塞                                    该线程去唤醒另一个在该信号量上等待的线程,然后继续执行
    
代码:
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>


#define N 5
#define sleepTime 2
//信号量使用的参数
pthread_mutex_t chopsticks [N];
pthread_mutex_t mutex;
sem_t r;
int philosophers[N] = { 0,1,2,3,4 };
//swap指令需要的参数
int islocked[N] = { 0 };
//互斥量使用的参数
pthread_mutex_t chops[N];

//交换函数 ,汇编指令

void xchg(int *x,int *y)
{
    __asm__("xchgl %0,%1":"=r" (*x) : "m" (*y));
}

//这个函数使用的解决方法是最多容许四个哲学家拿起左筷子

void philosopher(void * arg)
{
    int i = *(int *)arg;
    int left = i;
    int right = (i + 1) % N;
    int leftkey;
    int rightkey;

    while(1)
    {
        leftkey = 1;
        rightkey = 1;
        printf("哲学家 %d正在思考问题\n",i);
        sleep(sleepTime);
        printf("哲学家 %d饿了\n",i);
        sem_wait(&r);
        //56-60行   如果想要的筷子被占用,让该线程挂起,陷入死循环
        do{
            xchg(&leftkey,&islocked[left]);
        }while(leftkey);
        //筷子属于共享资源,不能同时被拿起,要加锁
        pthread_mutex_lock(&chopsticks[left]);
        printf("哲学家%d拿起了 %d号筷子,现在只有一支筷子,不能进餐\n",i,left);
        do{
            xchg(&rightkey,&islocked[right]);
        }while(rightkey);
        pthread_mutex_unlock(&chopsticks[right]);
        printf("哲学家%d拿起了 %d号筷子,现在有两只筷子,开始进餐\n",i,right);
        sleep(sleepTime);
        islocked[left] = 0;
        printf("哲学家%d放下了 %d号筷子\n",i,left);
        pthread_mutex_unlock(&chopsticks [left]);

        islocked[right] = 0;
        printf("哲学家%d放下了 %d号筷子\n",i,right);
        pthread_mutex_unlock(&chopsticks[right]);
        sem_post(&r);

    }
}
void philosopher2(void *arg)
{
    int i = *(int *)arg;
    int left = i;
    int right = ( i + 1 ) % N;
    while(1)
    {
        printf("哲学家%d 正在思考问题\n",i);
        sleep(sleepTime);
        printf("哲学家%d 饿了\n",i);
        if(i % 2 == 0 )   //偶数哲学家,先右后左
        {
            pthread_mutex_lock(&chopsticks [right]);
            printf("哲学家%d拿起了%d号筷子,现在有一支筷子,不能进餐\n",i,right);
            pthread_mutex_lock(&chopsticks[left]);
            printf("哲学家%d拿起了%d号筷子,现在有两支筷子,开始进餐\n",i,left);
            sleep(sleepTime);
            pthread_mutex_unlock(&chopsticks [right]);
            printf("哲学家%d放下了%d号筷子\n",i,right);
            pthread_mutex_unlock(&chopsticks [left]);
            printf("哲学家%d放下%d号筷子\n",i,left);
        }
        else    //奇数哲学家,先右后左
        {
            pthread_mutex_lock(&chopsticks[left]);
            printf("哲学家%d拿起了%d号筷子,现在有一支筷子,不能进餐\n",i,left);
            pthread_mutex_lock(&chopsticks[right]);
            printf("哲学家%d拿起了%d号筷子,现在有两支筷子,开始进餐\n",i,right);
            sleep(sleepTime);
            pthread_mutex_unlock(&chopsticks[right]);
            printf("哲学家%d放下了%d号筷子\n",i,right);
            pthread_mutex_unlock(&chopsticks [left]);
            printf("哲学家%d放下了%d号筷子\n",i,left);
        }

    }

}
//加入一把全局锁,是为了能让某一个线程得到想要的资源,尽可能满足这一个线程
//注意解锁位置
void philosopher3(void * arg)
{

    int i = *(int *)arg;
    int left = i;
    int right = (i + 1) % N;
    while(1)
    {
        printf("哲学家%d正在思考问题\n",i);
        sleep(sleepTime);
        printf("哲学家%d饿了\n",i);
        pthread_mutex_lock(&mutex);
        pthread_mutex_lock(&chopsticks[left]);
        printf("哲学家%d拿起了%d号筷子,现在只有一支筷子,不能进餐\n",i,left);
        pthread_mutex_lock(&chopsticks[right]);
        printf("哲学家%d拿起了%d号筷子,现在有两支筷子,开始进餐\n",i,right);
        pthread_mutex_unlock(&mutex);
        sleep(sleepTime);
        pthread_mutex_unlock(&chopsticks[left]);
        printf("哲学家%d放下了%d号筷子\n",i,left);
        pthread_mutex_unlock(&chopsticks[right]);
        printf("哲学家%d放下了%d号筷子\n",i,right);
    }
}
int main(void)
{
    pthread_t PHD[N];
    pthread_mutex_init(&mutex,NULL);
    sem_init(&r,0,4);
    for(int i = 0;i<N;i++)
    {
        pthread_mutex_init(&chopsticks[i],NULL);
    }
    for(int i = 0; i< N ;i++)
    {
        pthread_create(&PHD[i],NULL,(void *)philosopher3,&philosophers[i]);
    }
    for(int i = 0 ;i < N;i++)
    {
        pthread_join(PHD[i],NULL);
    }
    sem_destroy(&r);
    for(int i = 0;i < N;i++)
    {
        pthread_mutex_destroy(&chopsticks[i]);
    }
    pthread_mutex_destroy(&mutex);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值