生产者/消费者的进程同步模拟

操作系统实验二之进程同步模拟

代码:

#include"stdio.h"
#include"stdlib.h"
#include <process.h>
#include"windows.h"

#define P(sem) WaitForSingleObject(sem,INFINITE)//P操作,sem为指定线程,INFINITE为等待时间(无限等待)
#define V(sem) ReleaseSemaphore(sem,1,NULL)//V操作,增加信号量

typedef struct Node{
	int product;//产品,用整数表示产品序号
	struct Node *next;
}*buffer;

buffer head=(buffer)malloc(sizeof(struct Node));//初始化缓冲区链表,head->next=NULL
buffer tail=head;//tail始终指向链表尾部

static int products_ID=0;//产品序号
const int buf_size=10;//缓冲区大小
const int producer_num=1;//生产者数量
const int consumer_num=8;//消费者数量

HANDLE empty,occupy,mutex;//三个信号量:空缓冲区数,产品数,缓冲区的互斥使用

void add(){//向缓冲区加一个元素(链表尾)
	products_ID++;
	buffer p;
	p=(buffer)malloc(sizeof(struct Node));
	tail->next=p;
	tail=p;
}
void remove(){//读出并移除缓冲区的一个元素(链表头)
	buffer p0=head->next;
	//printf("%d",p0->product);
	head->next=p0->next;
}
void ProducerThread(void* param){//生产者
	int id=*(int*)param;
	while(1){
		P(empty);
		P(mutex);
		Sleep(500);
		printf("Producer_%d produced %d号产品\n",id,products_ID);
		add();
		V(mutex);
		V(occupy);
	}
}
void ConsumerThread(void* param){//消费者
	int id=*(int*)param;
	while(1){
		P(occupy);
		P(mutex);
		Sleep(500);
		printf("Consumer_%d consumed **%d号产品\n",id,products_ID-2);
		remove();
		V(mutex);
		V(empty);
	}
}
int main(){
	int i;
	int p_id[producer_num],c_id[consumer_num];

	//创建信号量
	occupy=CreateSemaphore(NULL,0,buf_size,NULL);//已有产品数量,区间为0-buf_size
	//1'安全属性指针,一般设置为NULL
	//2'信号量初值
	//3'允许的最大值
	//4'信号量名字,一般为NULL,表示信号量局限于一个进程中;若给予一个名,则其它进程也可以使用该信号量。

	empty=CreateSemaphore(NULL,buf_size,buf_size,NULL);//空余缓冲区数量

	mutex=CreateSemaphore(NULL,1,1,NULL);//互斥量

	for(i=0;i<producer_num;i++){
		p_id[i]=i+1;//生产者编号
		_beginthread(ProducerThread,0,p_id+i);//创建线程
		//_beginthread(void(*start_address)(void*),unsigned stack_size,void *arglist)
		//start_address:程序执行一个新线程的起始地址,一般就是执行某个函数,此参数就是函数名
        //stack_size:新线程的堆栈大小或0,一般为0
        //Arglist:传给新线程的变量清单或空,若不需传递,则这里就是NULL,若要传递,一般这里会是个指针
	}
	for(i=0;i<consumer_num;i++){
		c_id[i]=i+1;
		_beginthread(ConsumerThread,0,c_id+i);
	}
	while(getchar()=='\n')
		break;
	return 0;
}

实验内容:
2.1 实验目的

了解Windows环境下信号量的实现方法,进一步加深对进程同步机制的理解。

2.2 实验准备

(1)查资料学习如:创建线程的函数_beginthread( )、创建信号量函数CreateSemaphore( )等,了解这几个函数的使用方法。

(2)熟悉并理解生产者/消费者问题。

2.3 实验内容

(1)问题描述

编程实现生产者/消费者的同步问题。

(2)基本要求

    ²  利用创建的线程模拟进程;

    ²  需显示哪个生产者创建了哪一个产品,以及哪个消费者消费了哪个产品,即生产和消费的流水账。例如:

            n  生产者A生产了1号产品

            n  生产者B生产了2号产品

            n  消费者a消费了1号产品

² 通过设置不同生产者线程数和消费者线程数,并观察流水记录,从而判断你编写的同步算法的正确性。

2.4 实现提示

(1)设计在同一个进程地址空间内执行的多个线程:生产者线程和消费者线程;

(2)生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费;

(3)消费者线程从缓冲区中获得物品,然后释放缓冲区;

(4)生产者线程生产物品时,若无空缓冲区可用,生产者线程必须等待消费者线程释放出一个空缓冲区;

(5)消费者线程消费物品时,若无物品,消费者线程将被阻塞,直到新的物品被生产出来。

(6)需要使用如下三个信号量:

² 一个互斥信号量,用以阻止生产者线程和消费者线程同时操作缓冲区列表;

² 一个信号量,用于当缓冲区满时让生产者线程等待;

² 一个信号量,用于当缓冲区空时让消费者线程等待。

(7)学有余力的同学可同时实现在Linux操作系统下的生产者消费者问题。

(8)查资料熟悉如下函数和原语的使用方法(对于本实验的完成是至关重要的)

_beginthread函数说明

    _beginthread( void( *start_address)( void * ), unsigned stack_size, void *arglist)//创建线程

    start_address:程序执行一个新线程的起始地址,一般就是执行某个函数,此参数就是函数名

    stack_size:新线程的堆栈大小或0,一般为0

    Arglist:传给新线程的变量清单或空,若不需传递,则这里就是NULL,若要传递,一般这里会是个指针

信号量包含的几个操作原语:

CreateSemaphore(SecAttr, InitialCoutn,MaxCount, SemName)//创建一个信号量

    SecAttr:安全属性指针,一般设置为NULL

    InitialCoutn:信号量初值

    MaxCount:允许的最大值

    SemName:信号量名字,一般为NULL,表示信号量局限于一个进程中;若给予一个名,则其它进程也可以使用该信号量。

    例如:mutex =CreateSemaphore(NULL,1,1,NULL),则mutex就是信号量  

ReleaseSemaphore(Semaphore, ReleaseCount,PreviousCount )//释放信号量,就是V操作

    Semaphore:信号量

    ReleaseCount:递增数量。若为1,则每次增加1

    PreviousCount:先前的值,若不需要信号量上次的值,则这个参数可置为NULL  

WaitForSingleObject(hHandle,dwMilliseconds) //等待信号,就是P操作

    hHandle:对象句柄,可以指定一系列的对象,如Event、Job、Memoryresource notification、Mutex、Process、Semaphore、Thread、Waitabletimer等,在本实验中就是信号量。

    dwMilliseconds:定时时间间隔,单位为毫秒。若指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。若为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。若为INFINITE,对象被触发信号后,函数才会返回。
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个是很经典的问题 实验题目: 生产者消费者(综合性实验) 实验环境: 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(); else comsuerrun(); element=hasElement(readyhead); } printf("就绪队列没有进程\n"); if(hasElement(consumerhead)) { printf("消费者等待队列中有进程:\n"); display(consumerhead); } else { printf("消费者等待队列中没有进程\n"); } if(hasElement(producerhead)) { printf("生产者等待队列中有进程:\n"); display(producerhead); } else { printf("生产者等待队列中没有进程\n"); } }
模拟进程管理 #include <stdio.h> #include <malloc.h> //Can only be used in independent situation; //#define getmem(type) (type*)malloc(sizeof(type)) #define buffersize 5 int processnum=0;//the num of processes struct pcb { /* 定义进程控制块PCB */ int flag;//flag=1 denote producer;flag=2 denote consumer; int numlabel; char product; char state; struct pcb* processlink; }*exe=NULL,*over=NULL; typedef struct pcb PCB; PCB* readyhead=NULL,* readytail=NULL; PCB* consumerhead=NULL,* consumertail=NULL; PCB* producerhead=NULL,* producertail=NULL; //产品数量 int productnum=0; int full=0,empty=buffersize;// semaphore char buffer[buffersize];//缓冲区 int bufferpoint=0;//缓冲区指针 void linkqueue(PCB* process,PCB** tail); PCB* getq(PCB* head,PCB** tail); bool hasElement(PCB* pro); void display(PCB* p); void linklist(PCB* p,PCB* listhead); void freelink(PCB* linkhead); bool processproc(); bool waitempty(); bool waitfull(); void signalempty(); void signalfull(); void producerrun(); void comsuerrun(); bool hasElement(PCB* pro); void linklist(PCB* p,PCB* listhead) { PCB* cursor=listhead; while(cursor->processlink!=NULL){ cursor=cursor->processlink; } cursor->processlink=p; } void freelink(PCB* linkhead) { PCB* p; while(linkhead!=NULL){ p=linkhead; linkhead=linkhead->processlink; free(p); } } void linkqueue(PCB* process,PCB** tail) { if((*tail)!=NULL){ (*tail)->processlink=process; (*tail)=process; } else{ printf("队列未初始化!"); } } PCB* getq(PCB* head,PCB** tail) { PCB* p; p=head->processlink; if(p!=NULL){ head->processlink=p->processlink; p->processlink=NULL; if( head->processlink ==NULL ) (*tail)=head; } else return NULL; return p; } bool processproc() { int i,f,num; char ch; PCB* p=NULL; PCB** p1=NULL; printf("\n 请输入希望产生的进程个数?"); scanf("%d",&num); getchar(); // if(num>=100){ // printf("您怎么要产生这么多进程!Demands Denied!"); // return false; // } for(i=0;i<num;i++){ printf("\n 请输入您要产生的进程:输入1为生产者进程;输入2为消费者进程\n"); scanf("%d",&f); getchar(); p=(PCB*)malloc(sizeof(PCB)) ; if( !p) { printf("内存分配失败"); return false; } p->flag=f; processnum++; p->numlabel=processnum; p->state='w'; p->processlink=NULL; if(p->flag==1){ printf("您要产生的进程生产者,它是第%d个进程。请您输入您要该进程产生的字符!\n",processnum); scanf("%c",&ch); getchar(); p->product=ch; productnum++; printf("您要该进程产生的字符是%c \n",p->product); } else { printf("您要产生的进程消费者,它是第%d个进程。\n",p->numlabel); } linkqueue(p,&readytail); } return true; } bool waitempty() { if(empty<=0) { printf("进程%d:缓冲区存数,缓冲区满,该进程进入生产者等待队列\n",exe->numlabel); linkqueue(exe,&producertail); return false; } else{ empty--; return true; } } void signalempty() { PCB* p; if(hasElement(producerhead)){ p=getq(producerhead,&producertail); linkqueue(p,&readytail); printf("等待中的生产者进程进入就绪队列,它的进程号是%d\n",p->numlabel); } empty++; } bool waitfull() { if(full<=0) { printf("进程%d:缓冲区取数,缓冲区空,该进程进入消费者等待队列\n",exe->numlabel); linkqueue(exe,&consumertail); return false; } else{ full--; return true;} } void signalfull() { PCB* p; if(hasElement(consumerhead)){ p=getq(consumerhead,&consumertail); linkqueue(p,&readytail); printf("等待中的消费者进程进入就绪队列,它的进程号是%d\n",p->numlabel); } full++; } void producerrun() { if(!waitempty()) return; printf("进程%d开始向缓冲区存数%c\n",exe->numlabel,exe->product); buffer[bufferpoint]=exe->product; bufferpoint++; printf("进程%d向缓冲区存数操作结束\n",exe->numlabel); signalfull(); linklist(exe,over); } void comsuerrun() { if(!waitfull()) return; printf("进程%d开始向缓冲区取数\n",exe->numlabel); exe->product=buffer[bufferpoint-1]; bufferpoint--; printf("进程%d向缓冲区取数操作结束,取数是%c\n",exe->numlabel,exe->product); signalempty(); linklist(exe,over); } void display(PCB* p) { p=p->processlink; while(p!=NULL){ printf("进程%d,它是一个",p->numlabel); p->flag==1? printf("生产者\n"):printf("消费者\n"); p=p->processlink; } } bool hasElement(PCB* pro) { if(pro->processlink==NULL) return false; else return true; } void main() { char terminate; bool element; printf("你想开始程序吗?(y/n)"); scanf("%c",&terminate); getchar(); //Queue initialize; readyhead=(PCB*)malloc(sizeof(PCB)); if(readyhead==NULL) return; readytail=readyhead; readyhead->flag=3; readyhead->numlabel=processnum; readyhead->state='w'; readyhead->processlink=NULL; consumerhead=(PCB*)malloc(sizeof(PCB)); if(consumerhead==NULL) return; consumertail=consumerhead; consumerhead->processlink=NULL; consumerhead->flag=4; consumerhead->numlabel=processnum; consumerhead->state='w'; consumerhead->processlink=NULL; producerhead=(PCB*)malloc(sizeof(PCB)); if(producerhead==NULL) return; producertail=producerhead; producerhead->processlink=NULL; producerhead->flag=5; producerhead->numlabel=processnum; producerhead->state='w'; producerhead->processlink=NULL; over=(PCB*)malloc(sizeof(PCB)); if(over==NULL) return; over->processlink=NULL; while(terminate=='y') { if(!processproc()) break; 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(); else comsuerrun(); element=hasElement(readyhead); } printf("就绪队列没有进程\n"); if(hasElement(consumerhead)) { printf("消费者等待队列中有进程:\n"); display(consumerhead); } else { printf("消费者等待队列中没有进程\n"); } if(hasElement(producerhead)) { printf("生产者等待队列中有进程:\n"); display(producerhead); } else { printf("生产者等待队列中没有进程\n"); } printf("你想继续吗?(press 'y' for on)"); scanf("%c",&terminate); getchar(); } printf("\n\n 进程模拟完成.\n"); //Free the room; freelink(over); over=NULL; freelink(readyhead); readyhead=NULL; readytail=NULL; freelink(consumerhead); consumerhead=NULL; consumertail=NULL; freelink(producerhead); producerhead=NULL; producertail=NULL; getchar(); }
以下是一个使用Java语言实现的生产者/消费者模型的完整代码,实现了同步和互斥操作: ```java import java.util.LinkedList; import java.util.Queue; import javax.swing.JFrame; import javax.swing.JTextArea; public class ProducerConsumer extends JFrame { private JTextArea output; // 用于显示输出信息的文本区域 private Queue<Integer> buffer; // 缓冲区队列 private final int BUFFER_SIZE = 5; // 缓冲区大小 private final Object lock = new Object(); // 用于同步的锁对象 public ProducerConsumer() { super("Producer/Consumer Demo"); output = new JTextArea(); add(output); setSize(300, 300); setVisible(true); buffer = new LinkedList<Integer>(); new Producer().start(); new Consumer().start(); } public static void main(String[] args) { new ProducerConsumer(); } // 生产者线程 private class Producer extends Thread { public void run() { int item; while (true) { synchronized (lock) { // 如果缓冲区已满,则等待 while (buffer.size() == BUFFER_SIZE) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } item = produceItem(); buffer.add(item); output.append("Produced item " + item + "\n"); // 唤醒等待的消费者线程 lock.notifyAll(); } // 生产完一个物品后随机休眠一段时间 try { Thread.sleep((int) (Math.random() * 3000)); } catch (InterruptedException e) { e.printStackTrace(); } } } // 生产一个物品 private int produceItem() { return (int) (Math.random() * 100); } } // 消费者线程 private class Consumer extends Thread { public void run() { int item; while (true) { synchronized (lock) { // 如果缓冲区为空,则等待 while (buffer.size() == 0) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } item = buffer.remove(); output.append("Consumed item " + item + "\n"); // 唤醒等待的生产者线程 lock.notifyAll(); } // 消费完一个物品后随机休眠一段时间 try { Thread.sleep((int) (Math.random() * 3000)); } catch (InterruptedException e) { e.printStackTrace(); } } } } } ``` 在此代码中,我们使用了一个JTextArea对象来显示输出信息。缓冲区队列使用了Java集合框架中提供的LinkedList类,而同步和互斥操作则使用了Java中的synchronized关键字和wait()/notifyAll()方法。在程序运行时,我们创建了一个生产者线程和一个消费者线程,它们在同一个缓冲区队列上进行操作。当缓冲区已满时,生产者线程将等待,直到消费者线程取走一个物品,唤醒生产者线程;当缓冲区为空时,消费者线程将等待,直到生产者线程放入一个物品,唤醒消费者线程。通过这种方式,我们实现了生产者/消费者模型中的同步和互斥操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值