显示一行提示信息。
3、 相关基础知识
1) 生产者和消费者模型
本实验所使用的生产者和消费者模型具有如下特点:
(1) 本实验的多个缓冲区不是环形循环的,也不要求按顺序访问。生
产者可以把产品放到目前某一个空缓冲区中。
(2) 消费者只消费指定生产者的产品。
(3) 在测试用例文件中指定了所有的生产和消费的需求,只有当共享
缓冲区的数据满足了所有关于它的消费需求后,此共享缓冲区才可以作为空闲空间允许新的生产者使用。
(4) 本实验在为生产者分配缓冲区时各生产者必须互斥,此后各个生
产者的具体生产活动可以并发。而消费者之间只有在对同一产品进行消费时才需要互斥,同时它们在消费过程结束时需要判断该消费对象是否已经消费完毕并清除该产品。
2) 同步对象
在 Windows中,常见的同步对象有:信号量(Semaphore)、互斥量
(Mutex)、临界段(Critical Section)和事件(Event)等。本程序中用到了前三个。使用这些对象都可分为三个步骤,一是创建或者初始化;接着请求该同步对象,随即进入临界区,这一步对应于互斥量上的上锁;最后释放该同步对象,这对应于互斥量的解锁。这些同步对象在一个线程中创建,在其他线程中都可以使用,从而实现同步互斥。当然,在进程间使用这些同步对象实现同步的方法是类似的。
31
4、程序结构
主函数 初始化缓冲区、 消费请求队列及部分同步对象 提取线程信息 完成线程相关 同步对象的初始化 创建线程模拟 生产和消费 等待所有线程结束 程序结束
消费者 生产者 阻塞 N 有消费请求? N 存在空缓冲区? Y 阻塞 Y 阻塞 N 此请求可满足? Y 另一生产者正在写? Y N 确定产品位置 进入临界区(所有生产者之间互斥) 阻塞 此产品正被消费? Y 从空缓冲区中为本生产者的产品分配N 一个空间 进入临界区(对同一产品进行请求的消费者之间互斥) 退出临界区 消费产品、并判断是在该韩冲去中写入否应该释放产品所产品 占缓冲区 通过信号量通知等退出临界区 待本产品的消费者 结束消费线程 结束生产线程 32
5、 数据结构
a) 用一个整型数组Buffer_Critical来代表缓冲区。不管是生产产品还是对已
有产品的消费需要访问该组缓冲区。
b) 在程序中用一个自定义结构ThreadInfo记录一条线程的信息,即将测试
用例文件中的一行信息记录下来,用于程序创建相应的生产者或者消费者。由于要创建多个线程,所以程序中使用了一个ThreadInfo结构的数组Thread_Info。
c) 在实现本程序的消费生产模型时,具体地通过如下同步对象实现互斥: i.
设一个互斥量h_mutex,以实现生产者在查询和保留缓冲区内的下一个空位置时进行互斥。 ii.
每一个生产者用一个信号量与其消费者同步,通过设置h_Semaphore[MAX_THREAD_NUM]信号量数组实现,该组信号量用于表示相应产品已生产。同时用一个表示空缓冲区数目的信号量empty_semaphore进行类似的同步,指示缓冲区中是否存在空位置,以便开始生产下一个产品。 iii.
每一个缓冲区用一个同步对象实现该缓冲区上消费者之间的互斥,这通过设置临界区对象数组PC_Critical[MAX_BUFFER_NUM]实现。
6、实验环境
本实验是在win VC6.0环境下实现的,利用Windows SDK编制实例程序。
所以试验需要在windows下安装VC后进行。VC是一个集成开发环境,其中包含了Windows SDK所有工具和定义;所以安装了VC后就不用特意安装SDK了。
7、实现步骤
(1) 打开VC,选择菜单项file->new,选择projects选项卡并建立一个名为\的win32 console applicatoin工程;创建时注意指定创建该工程的目录; (2) 在工程中创建源文件\:选择菜单项project->add to project->files,在选择框中输入自己想要创建的文件名,这里是\;在接下来询问是
33
否创建新文件时回答\;然后通过Workspace->FileView->Source Files打开该文件,在其中编辑源文件并保存.
(3) 通过调用菜单命令项build->build all进行编译连接,可以在指定的工程目录下得到debug-> R_WP1.exe程序,然后把给定的test.txt文件存入该debug目录下,就可以在控制台进入该debug目录运行程序了。需要强调的是在创建数据文件时,由于涉及到文件格式问题,最好在记事本中手工逐个输入数据,而不要拷贝粘贴数据。
8、源程序
#include
//定义一些常量;
//本程序允许的最大临界区数; #define MAX_BUFFER_NUM 10 //秒到微秒的乘法因子; #define INTE_PER_SEC 1000
//本程序允许的生产和消费线程的总数; #define MAX_THREAD_NUM 64
//定义一个结构,记录在测试文件中指定的每一个线程的参数 struct ThreadInfo { int serial; //线程序列号 char entity; //是P还是C double delay; //线程延迟 int thread_request[MAX_THREAD_NUM]; //线程请求队列 int n_request; //请求个数 };
//全局变量的定义
//临界区对象的声明,用于管理缓冲区的互斥访问;
CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM];
int Buffer_Critical[MAX_BUFFER_NUM]; //缓冲区声明,用于存放产品;
34
HANDLE h_Thread[MAX_THREAD_NUM]; //用于存储每个线程句柄的数组;
ThreadInfo Thread_Info[MAX_THREAD_NUM]; //线程信息数组; HANDLE empty_semaphore; //一个信号量; HANDLE h_mutex; //一个互斥量; DWORD n_Thread = 0; //实际的线程的数目; DWORD n_Buffer_or_Critical; //实际的缓冲区或者临界区的数目;
HANDLE h_Semaphore[MAX_THREAD_NUM]; //生产者允许消费者开始消费的信号量;
//生产消费及辅助函数的声明 void Produce(void *p);
void Consume(void *p); bool IfInOtherRequest(int); int FindProducePositon(); int FindBufferPosition(int);
int main(void) { //声明所需变量; DWORD wait_for_all; ifstream inFile; //初始化缓冲区; for(int i=0;i< MAX_BUFFER_NUM;i++) Buffer_Critical[i] = -1 //初始化每个线程的请求队列; for(int j=0;j
35