典型的进程间通信IPC问题-生产者消费者问题

本文详细解释了生产者消费者问题的简易模型,并提供了避免死锁现象的方法。通过C语言实现,包括线程处理、互斥量声明、线程注册等基本操作。同时介绍了管程的概念及其在生产者消费者问题中的应用,最后讨论了死锁的避免策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本实例详细解释了生产者消费者问题的简易模型,对于同步互斥以及多线程处理此问题提出了一个较好的解决方案。

#include <stdio.h>
#include <pthread.h>
#define  MAX 10000000000    //定义缓冲区数量,就是生产品数量
pthread_mutex_t  the_mutex;
pthread_cond_t  condc,condp;
int buffer=0;

void *product(void *ptr)
{
    int i;
    for(i=1;i<MAX;++i)
    {
        pthread_mutex_lock(&the_mutex);//互斥使用缓冲区
        while(buffer!=0)
            pthread_cond_wait(&condp,&the_mutex);
        buffer=i;//数据放入缓冲区
        pthread_cond_signal(&condc);//唤醒消费者
        pthread_mutex_unlock(&the_mutex);//释放缓冲区
    }
    pthread_exit;
}

void *consumer(void *ptr)
{
    int i;
    for(i=1;i<MAX;++i)
    {
        pthread_mutex_lock(&the_mutex);//互斥加锁
        while(buffer==0)
            pthread_cond_wait(&condc,&the_mutex);
        buffer=0;
        pthread_cond_signal(&condp);//唤醒唤醒生产者
        pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);

}
int main(int argc,char ** argv)
{
    pthread_t  pro,con;
    pthread_mutex_init(&the_mutex,0);
    pthread_cond_init(&condp,0);
    pthread_cond_init(&condc,0);
    pthread_create(&con,0,consumer,0);
    pthread_create(&pro,0,product,0);
    pthread_join(pro,0);
    pthread_join(con,0);
    pthread_cond_destroy(&condc);
    pthread_cond_destroy(&condp);
    pthread_mutex_destroy(&the_mutex);
}

在main函数里面包含了线程处理问题时的基本操作,包含线程创建,互斥量声明,线程注册的基本API函数。本实例最多可参考《Unix环境高级编程》第十二章线程控制。

  • 但是,有一点,这样的模型有极大地可能会发生死锁现象。死锁造成的四个条件是
    • 形成互斥条件。每个资源要么已经分配了一个进程,要么就是可用的。
    • 占有和等待条件。已经得到了某个资源的进程可以请求新的资源。
    • 不可抢占条件,就是已经分配给一个进程的资源不能强制被抢占,他只能被占有他的进程显示主动的释放,
    • 环路等待条件,就是死锁发生时,系统一定有一个或两个以上的进程组成的一个环路,在循环等待资源。但是却没有释放资源。
    • 死锁发生是,四个条件肯定都满足。如果任何一条件不成立,那么也就不会发生死锁。

死锁如何避免呢?
同样有四种方式
- 忽略该问题,如果你忽略他,那么他也会忽略你,非著名鸵鸟算法。然而,这种想法也就是想想、
- 检测死锁并恢复,检测死锁是否发生,一旦发生,采取措施补救,亡羊补牢。
- 仔细对资源进行分配,动态避免死锁。
- 破坏死锁的四个必要条件,任意一个条件不满足那么,死锁就不会发生。

管程是什么?
- 一个管程是一个由过程,变量,及数据结构组成的一个集合。他们组成一个特殊的模块和软件包,程序可以在任意时刻调用管程中的过程,但他们不能再管程之外声明的过程中直接使用管程中的数据结构。另外,管程是概念语言,C语言不支持管程。

monitor example
    integer i;
    condition c;
    produce producer();


    end;
    producer  consumer();
end monitor;

以下是采用java实现管程方式的生产者消费者问题,说到底,其实就是采用了一个辅助类,

public class ProducerConsumer{
    static final int N=100;
    static producer p=new producer();
    static consumer c=new consumer();
    static our_monitor mon =new our_monitor();//创建新的管程
    public static void main (String args[]){
        p.start();
        c.start();
    }
    static class producer extend Thread {
        public  void run(){
            int item;
            while(true){
                item=produce_item();
                mon.insert(item);
            }
        }
        private int produce_item(){//生产活动中。。。}
    }
    static class consumer extend Thread{
        public void run(){
            int item ;
            while(true){
                item=mon.remove();
                consumer_item(item);
            }
        }
        private void consumer_item(int item){//消费。。。。}
    }

    static class our_monitor{
        private int buffer[]=new int[N];
        private int count=0,io=0,hi=0;//计数器和索引
        public synchronized void insert (int val){
            if(count==N) 
                go_to_sleep();
            buffer[hi]=val;
            hi=(hi+1)%N;
            count++;
            if(count==1)
                notify();
        }
        public synchronized int remove(){
            int val;
            if(count==0)
                go_to_sleep();
            val=buffer[io];
            io=(io+1)%N;
            count--;
            if(count=N-1)
                notify();//如果生产者在休眠,唤醒生产者
            return val;
        }
        private void go_to_sleep(){
            try{
                wait();
            }
            catch(interruptedException exc){}
        }
    }
}
实验题目: 生产者消费者(综合性实验) 实验环境: C语言编译器 实验内容: ① 由用户指定要产生的进程及其类别,存入进入就绪队列。    ② 调度程序从就绪队列中提取一个就绪进程运行。如果申请的资源被阻塞则进入相应的等待队列,调度程序调度就绪队列中的下一个进程。进程运行结束时,会检查对应的等待队列,激活队列中的进程进入就绪队列。运行结束的进程进入over链表。重复这一过程直至就绪队列为空。    ③ 程序询问是否要继续?如果要转直①开始执行,否则退出程序。 实验目的: 通过实验模拟生产者消费者之间的关系,了解并掌握他们之间的关系及其原理。由此增加对进程同步的问题的了解。 实验要求: 每个进程有一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程类型标号、进程系统号、进程状态、进程产品(字符)、进程链指针等等。 系统开辟了一个缓冲区,大小由buffersize指定。 程序中有三个链队列,一个链表。一个就绪队列(ready),两个等待队列:生产者等待队列(producer);消费者队列(consumer)。一个链表(over),用于收集已经运行结束的进程 本程序通过函数模拟信号量的操作。 参考书目: 1)徐甲同等编,计算机操作系统教程,西安电子科技大学出版社 2)Andrew S. Tanenbaum著,陈向群,马红兵译. 现代操作系统(第2版). 机械工业出版社 3)Abranham Silberschatz, Peter Baer Galvin, Greg Gagne著. 郑扣根译. 操作系统概念(第2版). 高等教育出版社 4)张尧学编著. 计算机操作系统教程(第2版)习题解答与实验指导. 清华大学出版社 实验报告要求: (1) 每位同学交一份电子版本的实验报告,上传到202.204.125.21服务器中。 (2) 文件名格式为班级、学号加上个人姓名,如: 电子04-1-040824101**.doc   表示电子04-1班学号为040824101号的**同学的实验报告。 (3) 实验报告内容的开始处要列出实验的目的,实验环境、实验内容等的说明,报告中要附上程序代码,并对实验过程进行说明。 基本数据结构: PCB* readyhead=NULL, * readytail=NULL; // 就绪队列 PCB* consumerhead=NULL, * consumertail=NULL; // 消费者队列 PCB* producerhead=NULL, * producertail=NULL; // 生产者队列 over=(PCB*)malloc(sizeof(PCB)); // over链表 int productnum=0; //产品数量 int full=0, empty=buffersize; // semaphore char buffer[buffersize]; // 缓冲区 int bufferpoint=0; // 缓冲区指针 struct pcb { /* 定义进程控制块PCB */ int flag; // flag=1 denote producer; flag=2 denote consumer; int numlabel; char product; char state; struct pcb * processlink; …… }; processproc( )--- 给PCB分配内存。产生相应的的进程:输入1为生产者进程;输入2为消费者进程,并把这些进程放入就绪队列中。 waitempty( )--- 如果缓冲区满,该进程进入生产者等待队列;linkqueue(exe,&producertail); // 把就绪队列里的进程放入生产者队列的尾部 void signalempty() bool waitfull() void signalfull() void producerrun() void comsuerrun() void main() { processproc(); element=hasElement(readyhead); while(element){ exe=getq(readyhead,&readytail); printf("进程%d申请运行,它是一个",exe->numlabel); exe->flag==1? printf("生产者\n"):printf("消费者\n"); if(exe->flag==1) producerrun();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值