《操作系统》课程设计
生产者和消费者问题
系 院:计算机科学系
学生姓名:吴伟
学 号:0734130101
专 业:软件工程
年 级:0701B
完成日期:2009 年 11 月
指导教师:刘栓
一、课程设计的性质与任务
1、生 产 者 -消 费 者 问 题 是 很 经 典 很 具 有 代 表 性 的 进 程 同 步 问 题 ,计 算 机 中 的
很 多 同 步 问 题 都 可 抽 象 为 生 产 者 -消 费 者 问 题 , 通 过 本 实 验 的 练 习 , 希 望 能
加 深 学 生 对 进 程 同 步 问 题 的 认 识 与 理 解 。
2、 熟 悉 VC 的 使 用 , 培 养 和 提 高 学 生 的 分 析 问 题 、 解 决 问 题 的 能 力 。
二、课程设计的内容及其要求
1. 实验内容
以生产者/消费者模型为依据,在 Windows 2000 环境下创建一个控制台进程,在该进程
中创建 n 个线程模拟生产者和消费者,实现进程(线程)的同步与互斥。
2. 实验要求
学习并理解生产者/消费者模型及其同步/互斥规则;
学习了解 Windows 同步对象及其特性;
熟悉实验环境,掌握相关 API 的使用方法;
设计程序,实现生产者/消费者进程(线程)的同步与互斥;
提交实验报告。
三、课程设计的时间安排
课程设计时间 8 课时
四、课程设计的实验环境
本实验是在 winxp+VC6.0 环境下实现的,利用 Windows SDK 编制实例程序。所以试验需要
在 windows 下安装 VC 后进行。VC 是一个集成开发环境,其中包含了 Windows SDK 所有工具
和定义;所以安装了 VC 后就不用特意安装 SDK 了。
五、正文
1、实验程序的结构图(流程图);
Wait Products
Consume
While consume
开始
Wait Buffer
Produce
While produce
结束
2、数据结构及信号量定义的说明;
(1) CreateThread
功能——创建一个在调用进程的地址空间中执行的线程
格式
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParamiter,
DWORD dwCreationFlags,
Lpdword lpThread
);
参数说明
lpThreadAttributes——指向一个 LPSECURITY_ATTRIBUTES(新线程的安全性描述符)。
dwStackSize——定义原始堆栈大小。
lpStartAddress——指向使用 LPTHRAED_START_ROUTINE 类型定义的函数。
lpParamiter——定义一个给进程传递参数的指针。
dwCreationFlags——定义控制线程创建的附加标志。
lpThread——保存线程标志符(32 位)
(2) CreateMutex
功能——创建一个命名或匿名的互斥量对象
格式
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName);
参数说明
lpMutexAttributes——必须取值 NULL。
bInitialOwner——指示当前线程是否马上拥有该互斥量(即马上加锁)。
lpName——互斥量名称。
(3) CreateSemaphore
功能——创建一个命名或匿名的信号量对象
格式
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName );
参数说明
lpSemaphoreAttributes——必须取值 NULL。
lInitialCount——信号量的初始值。该值大于 0,但小于 lMaximumCount 指定的最大值。
lMaximumCount——信号量的最大值。
lpName——信号量名称。
(4) WaitForSingleObject
功能——使程序处于等待状态,直到信号量 hHandle 出现(即其值大于等于 1)或超过
规定的等待时间
格式
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
参数说明
hHandle——信号量指针。
dwMilliseconds——等待的最长时间(INFINITE 为无限等待)。
(5) ReleaseSemaphore
功能——对指定信号量加上一个指定大小的量。成功执行则返回非 0 值
格式
BOOL ReleaseSemaphore(HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lppreviousCount );
参数说明
hSemaphore——信号量指针。
lReleaseCount——信号量的增量。
lppreviousCount——保存信号量当前值。
(6) ReleaseMutex
功能——打开互斥锁,即把互斥量加 1。成功调用则返回 0
格式
BOOL ReleaseMutex(HANDLE hMutex);
参数说明
hMutex——互斥量指针。
(7) InitializeCriticalSection
功能——初始化临界区对象
格式
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
参数说明
lpCriticalSection——指向临界区对象的指针。
(8) EnterCriticalSection
功能——等待指定临界区对象的所有权
格式
VOID enterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
参数说明
lpCriticalSection——指向临界区对象的指针。
(9) LeaveCriticalSection
功能——释放指定临界区对象的所有权
格式
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
参数说明
lpCriticalSection——指向临界区对象的指针。
3、实验的步骤;
(1) 打开 VC,选择菜单项 file->new,选择 projects 选项卡并建立一个名为" R_WP1"的 win32
console applicatoin 工程;创建时注意指定创建该工程的目录;
(2) 在工程中创建源文件" R_WP1.cpp":选择菜单项 project->add to project->files,在选择框
中输入自己想要创建的文件名,这里是" R_WP1.cpp";在接下来询问是否创建新文件时回答
"yes";然后通过 Workspace->FileView->Source Files 打开该文件,在其中编辑源文件并保存.
(3) 通 过调 用菜 单 命令 项 build->build all 进 行编 译连 接,可 以在 指定 的 工程 目 录下 得 到
debug-> R_WP1.exe 程序,然后把给定的 test.txt 文件存入该 debug 目录下,就可以在控制台
进入该 debug 目录运行程序了。需要强调的是在创建数据文件时,由于涉及到文件格式问题,
最好在记事本中手工逐个输入数据,而不要拷贝粘贴数据。
4、 主要算法
创建生产者和消费者线程
for(i =0;i< (int) n_Thread;i++){
if(Thread_Info[i].entity =='P')
h_Thread[i]= CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce),
&(Thread_Info[i]),0,NULL);
else
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consume),
&(Thread_Info[i]),0,NULL);
}
生产者进程
void Produce(void *p)
{
//局部变量声明;
DWORD wait_for_semaphore,wait_for_mutex,m_delay;
int
m_serial;
//获得本线程的信息;
m_serial = ((ThreadInfo*)(p))->serial;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);
Sleep(m_delay);
//开始请求生产
printf("Producer %2d sends the produce require.\n",m_serial);
//确认有空缓冲区可供生产,同时将空位置数 empty 减 1;用于生产者和消费者的同步;
wait_for_semaphore
= WaitForSingleObject(empty_semaphore,-1);
//互斥访问下一个可用于生产的空临界区,实现写写互斥;
wait_for_mutex = WaitForSingleObject(h_mutex,-1);
int ProducePos = FindProducePosition();
ReleaseMutex(h_mutex);
//生产者在获得自己的空位置并做上标记后,以下的写操作在生产者之间可以并发;
//核心生产步骤中,程序将生产者的 ID 作为产品编号放入,方便消费者识别;
printf("Producer %2d begin
to produce at position %2d.\n",m_serial,ProducePos);
Buffer_Critical[ProducePos] = m_serial;
printf("Producer %2d finish producing :\n ",m_serial);
printf("
//使生产者写的缓冲区可以被多个消费者使用,实现读写同步;
ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL);
position[ %2d ]:%3d \n" ,ProducePos,Buffer_Critical[ProducePos]);
}
消费者进程
void Consume(void * p)
{
//局部变量声明;
DWORD wait_for_semaphore,m_delay;
int m_serial,m_requestNum;
int m_thread_request[MAX_THREAD_NUM];//本消费线程的请求队列;
//消费者的序列号和请求的数目;
//提取本线程的信息到本地;
m_serial = ((ThreadInfo*)(p))->serial;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);
m_requestNum = ((ThreadInfo *)(p))->n_request;
for (int i = 0;ithread_request[i];
Sleep(m_delay);
//循环进行所需产品的消费
for(i =0;ithread_request[i] =-1;