1、实验目的
1) 回顾系统进程、线程的有关概念,加深对Windows 2000线程的理解。
2) 了解互斥体对象,通过对生产者消费者等进程间同步与互斥经典算法的实现,加深对P、V原语以及利用P、V原语进行进程间同步与互斥操作的理解。
2、实验内容和步骤
(1). 生产者消费者问题
步骤1:创建一个“Win32 Consol Application”工程,然后拷贝清单3-1中的程序,编译成可执行文件。 步骤2:在“命令提示符”窗口运行步骤1中生成的可执行文件。运行结果: 范例:E:\\课程\\os课\\os实验\\程序\\os11\\debug>os31 (假设编译生成的可执行文件是os31.exe)
__
步骤3:仔细阅读源程序,找出创建线程的WINDOWS API函数,回答下列问题:线程的第一个执行函数是什么(从哪里开始执行)?它位于创建线程的API函数的第几个参数中? DWORD WINAPI Producer(LPVOID lpPara) 第三个参数
hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]);___________________________________ 步骤4:修改清单3-1中的程序,调整生产者线程和消费者线程的个数,使得消费者数目大与生产者,看看结果有何不同。运行结果:
const unsigned short PRODUCERS_COUNT = 3; //生产者的个数 const unsigned short CONSUMERS_COUNT = 6; //消费者的个数
21
_______
___________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 从中你可以得出什么结论:
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
步骤5:修改清单3-1中的程序,按程序注释中的说明修改信号量EmptySemaphore的初始化方法,看看结果有何不同。运行结果:
EmptySemaphore=CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL);_改为 EmptySemaphore=CreateSemaphore(NULL,0,SIZE_OF_BUFFER-1,NULL);_
无法执行
___________________________________________________________________________________________________
22
步骤6:根据步骤4的结果,并查看MSDN,回答下列问题 1)CreateMutex中有几个参数,各代表什么含义。
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
2)CreateSemaphore中有几个参数,各代表什么含义,信号量的初值在第几个参数中。
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
3)程序中P、V原语所对应的实际Windows API函数是什么,写出这几条语句。 WaitForSingleObject(EmptySemaphore,INFINITE); P操作 WaitForSingleObject(Mutex,INFINITE); ReleaseMutex(Mutex); V操作
ReleaseSemaphore(FullSemaphore,1,NULL);
4)CreateMutex能用CreateSemaphore替代吗?尝试修改程序3-1,将信号量Mutex完全用CreateSemaphore及相关函数实现。写出要修改的语句:
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
(2). 读者写者问题
根据实验(1)中所熟悉的P、V原语对应的实际Windows API函数,并参考教材中读者、写者问题的算法原理,尝试利用Windows API函数实现第一类读者写者问题(读者优先)。
3、 实验结论
__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
4、 程序清单
清单3-1 生产者消费者问题 #include
const unsigned short SIZE_OF_BUFFER = 2; //缓冲区长度 unsigned short ProductID = 0; //产品号
unsigned short ConsumeID = 0; //将被消耗的产品号
unsigned short in = 0; //产品进缓冲区时的缓冲区下标 unsigned short out = 0; //产品出缓冲区时的缓冲区下标
23
int buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列 bool p_ccontinue = true; //控制程序结束 HANDLE Mutex; //用于线程间的互斥
HANDLE FullSemaphore; //当缓冲区满时迫使生产者等待 HANDLE EmptySemaphore; //当缓冲区空时迫使消费者等待
DWORD WINAPI Producer(LPVOID); //生产者线程 DWORD WINAPI Consumer(LPVOID); //消费者线程
int main() {
//创建各个互斥信号
//注意,互斥信号量和同步信号量的定义方法不同,互斥信号量调用的是CreateMutex函数,同步信号量 //调用的是CreateSemaphore函数,函数的返回值都是句柄。 Mutex = CreateMutex(NULL,FALSE,NULL);
EmptySemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL); //将上句做如下修改,看看结果会怎样
//EmptySemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER-1,NULL); FullSemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL);
//调整下面的数值,可以发现,当生产者个数多于消费者个数时, //生产速度快,生产者经常等待消费者;反之,消费者经常等待 const unsigned short PRODUCERS_COUNT = 3; //生产者的个数 const unsigned short CONSUMERS_COUNT = 1; //消费者的个数
//总的线程数
const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT;
HANDLE hThreads[THREADS_COUNT]; //各线程的handle
DWORD producerID[PRODUCERS_COUNT]; //生产者线程的标识符 DWORD consumerID[CONSUMERS_COUNT]; //消费者线程的标识符
//创建生产者线程
for (int i=0;i hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]); if (hThreads[i]==NULL) return -1; } //创建消费者线程 for (i=0;i hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,NULL,0,&consumerID[i]); if (hThreads[i]==NULL) return -1; } while(p_ccontinue){ if(getchar()){ //按回车后终止程序运行 p_ccontinue = false; } } 24 return 0; } //生产一个产品。简单模拟了一下,仅输出新产品的ID号 void Produce() { std::cout << std::endl<< \ std::cout << \} //把新生产的产品放入缓冲区 void Append() { std::cerr << \ buffer[in] = ProductID; in = (in+1)%SIZE_OF_BUFFER; std::cerr << \ //输出缓冲区当前的状态 for (int i=0;i //从缓冲区中取出一个产品 void Take() { std::cerr << \ ConsumeID = buffer[out]; buffer[out] = 0; out = (out+1)%SIZE_OF_BUFFER; std::cerr << \ //输出缓冲区当前的状态 for (int i=0;i //消耗一个产品 void Consume() 25