课程名称: 操作系统
实验名称: 生产者和消费者问题
学 号:
学生姓名:
班 级:
指导教师:
评 分:
实验日期:2012年 10月 22 日
1、实验目的:
掌握基本的同步互斥算法,理解生产者和消费者模型。
了解windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。
| 学习使用windows 2000/XP中基本的同步对象,掌握相应的API。 |
| 2、实验要求 (1)创建生产和消费线程 在windows2000环境下,创建一个控制台程序,在此进程中创建n个线程来模拟生产者或消费者。这些线程的信息由我们在本程序定义的“测试用例文件”中予以制定。该文件的格式如下: 3 1P 3 2P 4 3C 4 1 4P 2 5C 3 1 2 4 (2)生产和消费的规则 a.共享缓冲区存在空闲空间时,生产者即可用共享缓冲区。 b.只有当所有的消费者需求都被满足以后,该产品所在的共享缓冲区才能被释放,并作为空闲空间允许心的生产者适用。 c.每个消费者线程的各个消费需求之间存在先后顺序。 d.在每个县城发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息。 |
| 3、实验环境 硬件: CPU :AMD QL 内存: 2GB显卡:ATI 4570硬盘:日立250G 软件:Windows 2000/XP。 开发工具:VC++6.0。 |
| 4、实验内容 1)实现原理 a.生产者和消费者模型 b.同步对象 c.CreateThread的用法 功能:本API创建一个在调用进程的地址空间中执行的线程。 格式: HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); d.CreateMutex的用法 功能:本API产生一个命名的或者匿名的互斥量对象。 格式: HANDLE CreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全属性的指针 BOOLbInitialOwner, // 初始化互斥对象的所有者 LPCTSTRlpName // 指向互斥对象名的指针 ); e.CreateSemaphore的用法 功能:本API创建一个命名的或者匿名的信号量对象。 格式: HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTESlpSemaphoreAttributes, // SD LONGlInitialCount, // initial count LONGlMaximumCount, // maximum count LPCTSTRlpName // object name ); f.WaitForSingleObject的用法 功能:本API使程序处于等待状态,直到信号hHandle出现或者超出规定的等待最长时间,信号量出现指信号量大于或等于1。 格式:DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); g.InitializeCriticalSection的用法 功能: 该函数初始化临界区对象。 格式: VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); h.ReleaseMutex的用法 功能:用来打开互斥锁,即将互斥量加1,。成功调用则返回0。 格式:BOOL ReleaseMutex ( HANDLE hMutex ); i.EnterCriticalSection 的用法: 功能:用于等待之的呢过临界区对象的所有权。 格式: VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); j.LeaveCriticalSection的用法 功能:释放指定临界区对象的所有权。 格式: VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); 2)程序结构 3)数据结构 (1)用一个整型数组Buffer_Critical来代表缓冲区。不管是生产产品还是对已有产品的消费都需要访问该组缓冲区。 (2)在程序中用一个自定义结构Threadlnfo记录一条线程的信息,即将测试用例文件中的一行信息记录下来,用于程序创建相应的生产者或者消费者。由于要创建多个线程,所以程序中使用了一个Threadlnfo结构的数组Thread lnfo。 (3)在实现本程序的消费生产模型时,具体地通过如下同步对象实现互斥:一个互斥量h_mutex,以实现生产者在查询和保留缓冲区内的下一个空位置时进行互斥。每一个生产者用一个信号量与其消费者同步,通过设置h _Semaphore[MAXTHREAD_NUM]信号量数组实现,该组信号量用于表示相应产品已生产。同时用一个表示空缓冲区数目的信号量empty semaphore进行类似的同步,指示缓冲区中是否存在空位置,以便开始生产下一个产品。每一个缓冲区用一个同步对象实现该缓冲区上消费者之间的互斥,这通过设置临界区对象数组PC_Critical[MAX_BUFFER_NUM]实现。 4)实现步骤 (1)打开VC,选择菜单项文件->新建,选择工程选项卡并建立一个名为R-WP1的Win32 console Application工程。 (2)在工程中创建源文件R-WP1.:选择菜单项文件->新建,选择文件选项卡并建立一个名为R-WP1.cpp的C++ Source Files文件,进行编译间接,得到R-WP1.exe程序。 (3)打开工程目录下的debug,再打开test.txt,输入实验数据,并保存。 (4)最后在进行编译连接。并把实验结果截图保存。 |
| 5、实验测试及分析: 结果分析: 1、在每个程序中坐须先做P(mutex),后做V(mutex),二者要成对出现。夹在二者中间的代码段就是该进程的临界区。 2、对同步信号量full和empty的P,V操作同样必须成对出现,但它们分别位于不同的程序中。 3、无论在生产者进程中还是在消费者进程中,两个P操作的次序不能颠倒:应先执行同 步信号量的P操作,然后执行互斥信号量的P操作。否则可能造成进程死锁。 |
| 6、实验心得体会 1)在添加test.txt文件时,需在R-WP1及debug文件夹下分别储存才能实现程序正常运行。 2)使用vc++6.0创建工程,应当针对程序选择相应的工程类型,例如基于MFC的: MFC AppWizard(exe),或者基于控制台的:Win32 Console Application,这两种是编写普通程序时最常用。 3)学会用vc++6.0编写使用系统c语言程序,基本功不扎实,程序输入不够细心。 |
#include #include #include #include #include #define MAX_BUFFER_NUM 10 #define INTE_PER_SEC 1000 #define MAX_THREAD_NUM struct ThreadInfo { int serial; char entity; double delay; int thread_request[MAX_THREAD_NUM]; int n_request; }; CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM]; int Buffer_Critical[MAX_BUFFER_NUM]; 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 Thread_Info[j].n_request = 0; } for(i =0;i< MAX_BUFFER_NUM;i++) InitializeCriticalSection(&PC_Critical[i]); inFile.open("test.txt"); inFile >> n_Buffer_or_Critical; inFile.get(); printf("输入文件是:\\n"); printf("%d \\n",(int) n_Buffer_or_Critical); while(inFile){ inFile >> Thread_Info[n_Thread].serial; inFile >> Thread_Info[n_Thread].entity; inFile >> Thread_Info[n_Thread].delay; char c; inFile.get(c); while(c!='\\n'&& !inFile.eof()){ inFile>> Thread_Info[n_Thread].thread_request[Thread_Info [n_Thread].n_request++]; inFile.get(c); } n_Thread++; } for(j=0;j<(int) n_Thread;j++){ int Temp_serial = Thread_Info[j].serial; char Temp_entity = Thread_Info[j].entity; double Temp_delay = Thread_Info[j].delay; printf(" \\n thread%2d %c %f ",Temp_serial,Temp_entity,Temp_delay); int Temp_request = Thread_Info[j].n_request; for(int k=0;k cout< printf("\\n\\n"); empty_semaphore=CreateSemaphore (NULL,n_Buffer_or_Critical,n_Buffer_or_Critical, "semaphore_for_empty"); h_mutex = CreateMutex(NULL,FALSE,"mutex_for_update"); for(j=0;j<(int)n_Thread;j++){ std::string lp ="semaphore_for_produce_"; int temp =j; while(temp){ char c = (char)(temp%10); lp+=c; temp/=10; } h_Semaphore[j+1]=CreateSemaphore(NULL,0,n_Thread,lp.c_str()); } 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); } wait_for_all = WaitForMultipleObjects(n_Thread,h_Thread,TRUE,-1); printf(" \\n \\nALL Producer and consumer have finished their work. \\n"); printf("Press any key to quit!\\n"); _getch(); return 0; } bool IfInOtherRequest(int req) { for(int i=0;i return TRUE; return FALSE; } int FindProducePosition() { int EmptyPosition; for (int i =0;i EmptyPosition = i; Buffer_Critical[i] = -2; break; } return EmptyPosition; } int FindBufferPosition(int ProPos) { int TempPos; for (int i =0 ;i TempPos = i; break; } return TempPos; } 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); wait_for_semaphore = WaitForSingleObject(empty_semaphore,-1); wait_for_mutex = WaitForSingleObject(h_mutex,-1); int ProducePos = FindProducePosition(); ReleaseMutex(h_mutex); 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(" position[ %2d ]:%3d \\n" ,ProducePos,Buffer_Critical [ProducePos]); ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL); } 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;i Sleep(m_delay); for(i =0;i wait_for_semaphore=WaitForSingleObject(h_Semaphore[m_thread_request[i]],-1); int BufferPos=FindBufferPosition(m_thread_request[i]); EnterCriticalSection(&PC_Critical[BufferPos]); printf("Consumer%2d begin to consume %2d product \\n",m_serial,m_thread_request[i]); ((ThreadInfo*)(p))->thread_request[i] =-1; if(!IfInOtherRequest(m_thread_request[i])){ Buffer_Critical[BufferPos] = -1; printf("Consumer%2d finish consuming %2d:\\n ",m_serial,m_thread_request[i]); printf(" position[ %2d ]:%3d \\n" ,BufferPos,Buffer_Critical[BufferPos]); ReleaseSemaphore(empty_semaphore,1,NULL); } else{ printf("Consumer %2d finish consuming product %2d\\n ",m_serial,m_thread_request[i]); } LeaveCriticalSection(&PC_Critical[BufferPos]); } }下载本文