} WaitForSingleObject(hMutex, INFINITE) ; i--; ReleaseMutex(hMutex) ; } return j; int main() { DWORD id1,id2; HANDLE hThread[2] ; hMutex = CreateMutex( NULL, // 缺省的安全属性,句柄不被子进程继承 FALSE, NULL) ; // 只初始化,不拥有 // 匿名的 hThread[0] = CreateThread(0,0,fun1,0,0,&id1); hThread[1] = CreateThread(0,0,fun2,0,0,&id2); WaitForMultipleObjects(2,hThread,1,INFINITE); cout<<\ ReleaseMutex(hMutex) ; CloseHandle(hMutex) ; } return 0; 程序4_4: # include # include HANDLE g_hSemThreads=INVALID_HANDLE_VALUE; static DWORD WINAPI ThreadProc(LPVOID lpParam) { LONG nPauseMs=reinterpret_cast(lpParam); Sleep(nPauseMs*100); if(g_hSemThreads!=INVALID_HANDLE_VALUE) { LONG nPrevCt(0); if(ReleaseSemaphore(g_hSemThreads,// 要增加的信号量句柄 1,// 增加的计数 &nPrevCt)) //增加前的数值返回 { cout<< nPauseMs<< \ } } return(0); } void main() { g_hSemThreads=CreateSemaphore(NULL,// 是信号量的安全属性 5, //是初始化的信号量 5, //信号量的最大值 NULL); //信号量的名称 for(int nTotal=10;nTotal>0; nTotal--) { WaitForSingleObject(g_hSemThreads,INFINITE); LONG nPauseMs=nTotal*5; Sleep(100); HANDLE Thread=CreateThread(NULL,0,ThreadProc,reinterpret_cast(nPauseMs),0,NULL); cout<< nPauseMs<< \ CloseHandle(hThread); hThread=INVALID_HANDLE_VALUE; } CloseHandle(g_hSemThreads); g_hSemThreads=INVALID_HANDLE_VALUE; } 思考题 1. 比较临界区和互斥体实现同步的实用方法,及程序执行的效率。 临界区是一种最直接的线程同步方式。所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10 ~ 15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。当一个互斥对象不再被一个线程所拥有,它就处于发信号状态。 2. 分析信号量对象的实用情况和方法,分析创建信号量对象的API函数各参数的含义。 信号量对象。它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。 CreateSemaphore() 创建一个信号量 OpenSemaphore() 打开一个已经创建的信号量 ReleaseSemaphore() 释放对信号量的所有权
实验小结 实验4 (一) 每个实验都能认认真真完成,从实验中体会到同步机制的重要性。能够认真学习API函数,并将自己所学的知识运用到实验中。 (二) 对于一些效率要求很高的程序,不适宜用Windows API,而应该自己编写效率较高的线程同步算法,就目前而言,这还是我比较欠缺的,急需要锻炼的能力。 (三) 通过这次实验,体会到同步机制的重要性,掌握了有关线程同步的一些知识:两个或多个线程争相执行同一段代码或访问同一资源的现象称为竞争;可能造成竞争的共享代码段或资源就被称为临界区;在任何时刻都能有一个线程在临界区中的现象被称为互斥。 实验3 通过本次实验,了解了在Windows系统中,进程是资源的拥有者,线程是系统调度的单位。进程创建后,其主线程也随即被创建。了解了线程的引入是为了减少程序并发执行时所付出的时空开销,使得并发粒度更细,并发更好。这个思路下,“独立分配资源”的任务仍由进程完成,作为系统资源保护和分配的基本单位,而“被调度分派执行”的任务被交付给线程的实体完成,线程作为系统调度和分配的基本单位,会被频繁的调度和切换。 与进程相比,线程的有点为: 快速线程切换,同一个进程中的多线程切换,只需要改变堆栈和寄存器,地址空间不用变。 通信易于实现。自动共享进程的内存和文件,线程可以自由访问全局变量,通信十分方便,而且不需要通过内核 减少管理开销。线程创建和撤销的工作比进程少。 并发程度高。多线程适合并行工作 备注: