一、实验目的 1.掌握基本的同步互斥算法,理解生产者和消费者模型。 2.了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 3.学习使用Windows 2000/XP中基本的同步对象,掌握相应的API函数。
二、实验内容及要求 1.生产者消费者对缓冲区进行互斥操作。 2.缓冲区大小为10,缓冲区满则不允许生产者生产数据,缓冲区空则不允许消费者消费数据。 3.生产者消费者循环操作可随时终止。
三、实验条件(可含设备、材料、工具、软件等) 一台计算机,vc++环境
四、实验原理(可含实验思想、原理图、程序框图等) 1.互斥量对象(mutex) 互斥对象能够确保线程拥有对单个资源的互斥访问权。互斥对象包含一个引用数量,一个线程I D和一个递归计数器。 互斥对象的使用规则如下: • 如果线程I D是0(这是个无效I D),互斥对象不被任何线程所拥有,互斥对象处于“受信”状态。 • 如果I D是个非0数字,那么一个线程就拥有互斥对象,互斥对象处于“未受信”状态。 函数原型
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // pointer to security attributes BOOL bInitialOwner, // flag for initial ownership LPCTSTR lpName // pointer to mutex-object name );作用:创建一个命名或无名互斥对象。例:
hHandle= CreateMutex(NULL,false,NULL);创建一个无名互斥对象,放弃拥有权(ownership),此时互斥量处于“受信”状态。
HANDLE OpenMutex( DWORD dwDesiredAccess, // access flag BOOL bInheritHandle, // inherit flag LPCTSTR lpName // pointer to mutex-object name );作用:打开一个命名的互斥对象。
/ / BOOL ReleaseMutex( HANDLE hMutex // handle to mutex object );作用:释放一个互斥对象,该函数同时将对象的递归计数器递减1。当递归计数器到达0时,该线程I D也被置为0,同时该对象变为“受信”状态。 说明:不同于其他内核对象,互斥对象有一个“线程所有权”的概念。当一个线程调用ReleaseMutex函数释放互斥对象时,该函数要查看调用线程的I D是否与互斥对象中的线程I D相匹配。如果两个I D相匹配,递归计数器就会递减;如果两个线程的I D不匹配,那么ReleaseMutex函数将不进行任何操作,而是将FALSE(表示失败)返回给调用者。
2.事件对象 事件对象能够通知一个操作已经完成。在所有的内核对象中,事件内核对象是个最基本的对象。它们包含一个使用计数,一个用于指明该事件是个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于“受信”状态还是“未受信”状态的布尔值。
函数原型
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // pointer to security attributes BOOL bManualReset, // flag for manual-reset event BOOL bInitialState, // flag for initial state LPCTSTR lpName // pointer to event-object name );作用:创建一个事件对象。 说明:有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。
HANDLE OpenEvent( DWORD dwDesiredAccess, // access flag BOOL bInheritHandle, // inherit flag LPCTSTR lpName // pointer to event-object name );作用:打开一个事件对象。
BOOL SetEvent( HANDLE hEvent // handle to event object );作用:将事件对象设置为“受信”状态。 说明:当人工重置的事件为“受信”时,等待该事件的所有线程均变为可调度线程;当一个自动重置的事件为“受信”时,等待该事件的线程中只有一个线程变为可调度线程。
BOOL ResetEvent( HANDLE hEvent // handle to event object );作用:将事件对象设置为“未受信”状态。 说明:Microsoft为自动重置的事件定义了应该成功等待的副作用规则,即当线程成功地等待到该对象时,自动重置的事件就会自动重置到“未受信”状态。通常没有必要为自动重置的事件调用ResetEvent函数,因为系统会自动对事件进行重置。但是,Microsoft没有为人工重置的事件定义成功等待的副作用,即线程必须使用ResetEvent才能使得重置到“未受信”状态。
五、实验过程(可含操作步骤、现象、结果、参考代码等) 主要代码:
int m=10; //仓库可承载量 int in=0,out=0;//in是进入量,out是输出量 int a=0; void producer(){ if(a==m){ printf("对不起!仓库已满"); } else{ printf("请输入你要存放的数量: "); scanf("%d",&in); a=a+in; if(a>m) { printf("你可存放%d个",(m-a+in)); a=m; } else{ printf("你已存放%d个",in); } } } void consumer(){ if(a==0){ printf("对不起!仓库暂无库存"); } else{ printf("请输入你需要的数量:"); scanf("%d",&out); a=a-out; if(a<0){ printf("不好意思!仓库没有那么多库存,只可提供%d个", (a+out)); } else{ printf("请稍等!可为您提供%d个", out); } } } void main(){ int d; while(1){ printf("请输入你要测试的服务:"); scanf("%d",&d); switch(d){ case 0: consumer(); printf("\n"); break; case 1: producer(); printf("\n"); break; default: printf("请重新输入!"); } } getch();