




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、实验2 Windows线程同步和互斥实验目的1、了解Windows内核对线程同步的支持。2、了解C的线程函数库及Windows 基本的线程API 函数的使用。3、进一步理解线程的同步控制原理。预备知识一、Windows线程同步机制(注:互斥是同步的一种特例)n 事件(Event)n 临界区(Critical Section)n 互斥量(Mutex)n 信号量(Semaphore)1、是否能跨进程使用?互斥量、信号量、事件都可以跨进程来实现同步数据操作。临界区只能用在同一进程的线程间互斥,因为临界区无名(无句柄)。如果只为了在进程内部用的话,使用临界区会带来速度上的优势并能够减少资源占用量。2、
2、其它区别临界区:访问临界资源的代码段。课堂上讲过。(存钱、取钱的例子还记得吗?)互斥量:资源独占使用信号量:资源计数器事件对象:可以通过“通知”的方式来保持线程的同步。事件是WIN32中最灵活的线程间同步机制。事件存在两种状态:激发状态(Signaled or True)未激发状态(Unsignaled or False)。3、详细解释:(见下面实验内容每个程序前)二、VC+(略)实验内容1、用事件(Event)对象来进行线程同步n 事件可分为两类:n 手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。n 自动恢复:一旦事
3、件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。n _beginthread函数:创建一个线程。所在库文件:#include uintptr_t _beginthread( void( *start_address )( void * ), unsigned stack_size, void *arglist ); 返回值: 假如成功,函数将返回一个处理信息对这个新创建的线程。如果失败_beginthread将返回-1。 start_address 新线程的起始地址 ,指向新线程调用的函数的起始地址stack_size stack_size 新线程的堆栈大小,可以为0arglist
4、arglist 传递给线程的参数列表,无参数是为NULL n CreateEvent函数:创建事件对象 windows.hHANDLE CreateEvent(/ SECURITY_ATTRIBUTES结构指针,可为NULLLPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset, / 手动/自动/ TRUE:在WaitForSingleObject后必须手动调用ResetEvent清除信号/ FALSE:在WaitForSingleObject后,系统自动清除事件信号BOOL bInitialState, /初始状态LPCTSTR l
5、pName /事件的名称);n 使用“事件”机制应注意以下事项:n 如果跨进程访问事件,必须对事件命名,在对事件命名的时候,要注意不要与系统命名空间中的其它全局命名对象冲突;n 事件是否要自动恢复;n 事件的初始状态设置。n 由于event对象属于内核对象,故进程B可以调用OpenEvent函数通过对象的名字获得进程A中event对象的句柄,然后将这个句柄用于ResetEvent、SetEvent和WaitForMultipleObjects等函数中。此法可以实现一个进程的线程控制另一进程中线程的运行,例如: HANDLE hEvent=OpenEvent(EVENT_ALL_ACCESS,t
6、rue,MyEvent); ResetEvent(hEvent);验证程序:3个线程。主线程创建2个线程。一读,一写。写线程(并不真写,只是输出writing等字符串)完成后,读线程才能读,读线程完成后,主线程才能结束。新建一个Win32控制台应用程序项目(win32 console application)#include stdafx.h#include #include #include #include HANDLE evRead,evFinish;/全局变量,事件对象的句柄void ReadThread(LPVOID param) WaitForSingleObject(evRead
7、, INFINITE);/等待evRead被激活coutReading.读完成,唤醒主线程endl;SetEvent(evFinish);/激活evFinish事件void WriteThread(LPVOID param) coutWriting.写完成,唤醒读线程endl;SetEvent(evRead);/激活evRead事件int main(int argc, char* argv)evRead= CreateEvent(NULL,FALSE,FALSE,NULL);evFinish= CreateEvent(NULL,FALSE,FALSE,NULL);_beginthread(Re
8、adThread,0,NULL);_beginthread(WriteThread,0,NULL);WaitForSingleObject(evFinish, INFINITE);/等待evFinish被激活coutEnd.endl;return 0;如果引入了,还有如下错误error C2065: _beginthread : undeclared identifierError executing cl.exe.解决:工程设置c/c+标签分类(Category)下拉列表里选择代码生成(Code Generation) 选用运行时库(Use Run-Time Library)下拉列表里选择多
9、线程Multithreaded。然后重新编译。若还不行,再选Multithreaded DLL。验证:用/将两条WaitForSingleObject语句屏蔽。重新编译运行,多运行几次,看结果有何不同。思考原因。2、用临界区(Critical Section)来进行线程互斥n 临界区是保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。n 如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。 n 临
10、界区包含两个操作原语:n EnterCriticalSection() 进入临界区 n LeaveCriticalSection() 离开临界区 n EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。否则,临界区保护的共享资源将永远不会被释放。n 虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。 n 创建临界区为了创建临界区,首先必须在进程中分配一个全局CRITICAL_SECTION数据结构:CRITICAL_SECTION gCriti
11、calSection;n 使用临界区使用临界区之前,必须调用InitializeCriticalSection函数初始化:VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);n 进入临界区调用EnterCriticalSection函数进入临界区:VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);n 离开临界区调用LeaveCriticalSection函数退出了临界区:VOID LeaveCriticalSection(LPCRI
12、TICAL_SECTION lpCriticalSection);n 删除临界区调用DeleteCriticalSection函数删除临界区:VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);n 临界区一般用法:EnterCriticalSection(& gCriticalSection );/do somethingLeaveCriticalSection(& gCriticalSection);n 关于临界区的使用,有下列注意点:n 每个共享资源使用一个CRITICAL_SECTION变量;n 不要长时间运行
13、关键代码段,当一个关键代码段长时间运行时,其他线程就会进入等待状态,这会降低应用程序的运行性能;n 如果需要同时访问多个资源,则可能连续调用EnterCriticalSection;n Critical Section不是OS核心对象,如果进入临界区的线程挂了,将无法释放临界资源。这个缺点在Mutex中得到了弥补。验证程序:一个银行系统中两个线程对同一账户执行取款操作,余额1000元。一个使用ATM机取900元,另一个使用存折在柜台取700元。如果不加于控制,会使得账户余额为负数。#include stdafx.h#include #include #include #include int
14、total =1000;HANDLE evFin2;CRITICAL_SECTION cs;void WithDrawThread1(LPVOID param) /取900元EnterCriticalSection(&cs);if (total-900) = 0) total-=900; cout你取了900元 endl; else cout钱不够了,禁止取钱,马上退卡!= 0) total-=700; cout你取了700元 endl; else cout钱不够了,禁止取钱!endl; LeaveCriticalSection(&cs);SetEvent(evFin1);int main(i
15、nt argc, char* argv)evFin0 = CreateEvent(NULL,FALSE,FALSE,NULL);evFin1 = CreateEvent(NULL,FALSE,FALSE,NULL);InitializeCriticalSection(&cs);_beginthread(WithDrawThread1,0,NULL);_beginthread(WithDrawThread2,0,NULL);WaitForMultipleObjects(2, evFin, TRUE, INFINITE);DeleteCriticalSection(&cs);cout余额是tota
16、lendl;return 0;多运行几次,观察结果并分析。3、用互斥量(Mutex)来进行线程互斥n 互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限。n 由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。n 当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。n 互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。 用CreateMutex函数创建互斥量: HANDLE CreateMutex(LPSECURITY_AT
17、TRIBUTES lpMutexAttributes,/ 安全属性结构指针,可为NULLBOOL bInitialOwner, /是否占有该互斥量,TRUE:占有,FALSE:不占有LPCTSTR lpName /信号量的名称);涉及到的其它API如下:打开一个互斥量 :OpenMutex函数:HANDLE OpenMutex(DWORD fdwAccess,/ 值为SYNCHRONIZE或MUTEX_ALL_ACCESSBOOL fInherit,LPTSTR lpszName);释放一个互斥量 : ReleaseMutex函数:BOOL ReleaseMutex(HANDLE hMutex
18、);该函数将互斥量从无信号状态变到有信号状态。互斥和临界区的比较如下图: 特性互斥临界区运行速度 慢 快 是否能够跨进程边界来使用 是 否 声明 HANDLE hmtx ; CRITICAL_SECTION cs ; 初始化 h m t x = C r e a t e M u t e x ( N U L L , FA L S E , N U L L ); I n i t i a l i z e C r i t i c a l S e c t i o n ( & e s ) ; 清除 C l o s e H a n d l e ( h m t x ); D e l e t e C r i t i
19、 c a l S e c t i o n ( & c s ); 无限等待 Wa i t F o r S i n g l e O b j e c t ( h m t x , I N F I N I T E ); E n t e r C r i t i c a l S e c t i o n ( & c s ); 0 等待 Wa i t F o r S i n g l e O b j e c t ( h m t x , 0 ); Tr y E n t e r C r i t i c a l S e c t i o n ( & c s ); 任意等待 Wa i t F o r S i n g l e
20、 O b j e c t ( h m t x , d w M i l l i s e c o n d s ); 不能 释放 R e l e a s e M u t e x ( h m t x ); L e a v e C r i t i c a l S e c t i o n ( & c s ); 是否能够等待其他内核对象 是 (使用 Wa i t F o r M u l t i p l e O b j e c t s 或类似的函数) 否 验证程序:主线程创建3个线程。它们各自要输出一些字符串。#include stdafx.h#include #include #define THREAD_
21、INSTANCE_NUMBER 3 /主线程要创建3个线程LONG g_RessourceInUse=FALSE;LONG g_iCounter=0;void ThreadProc(void *pData) int ThreadNumTemp=(*(int *) pData);HANDLE hMutex;coutThreadProc:ThreadNumTemp in Running!endl;if (hMutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,Mutex.Test)=NULL) coutOpen mutex error!endl;WaitForSingleO
22、bject(hMutex,INFINITE);coutThreadProc:ThreadNumTemp gets the mutex.endl;ReleaseMutex(hMutex);CloseHandle(hMutex);coutThreadProc:ThreadNumTemp end.endl;int main(int argc, char* argv)DWORD IDTHREAD_INSTANCE_NUMBER;/数组,存放3个线程的ID号码(CreateThread返回)HANDLE hTHREAD_INSTANCE_NUMBER;/数组,存放3个线程的句柄HANDLE hMutex
23、;/互斥量的句柄int i;if (hMutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,Mutex.Test)=NULL) if (hMutex = CreateMutex(NULL,FALSE,Mutex.Test )= NULL) coutCreate Mutex error!endl;return 0;for (i=0;iTHREAD_INSTANCE_NUMBER;i+)hi=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,(void*)&IDi,0,&(IDi);if (hi=NULL) cou
24、t创建线程失败!endl; else cout创建线程成功 IDiendl;WaitForMultipleObjects(THREAD_INSTANCE_NUMBER,h,true,INFINITE);coutClose the mutex handle!endl;return 0;思考:输出多样,哪里体现了互斥量的作用?4、用信号量(Semaphore)来实现线程同步n 信号量是维护0到指定最大值之间的同步对象。n 信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。n 信号量对象在控制上可以支持有限数量共享资源的访问。n 创建信号量:HANDLE CreateSemaphore
25、 (PSECURITY_ATTRIBUTE psa,LONG lInitialCount, /开始时可供使用的资源数LONG lMaximumCount, /最大资源数 PCTSTR pszName);n 撤销信号量:BOOL WINAPI ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,/信号量的当前资源数增加lReleaseCountLPLONG lpPreviousCount); n 打开信号量HANDLE OpenSemaphore (DWORD fdwAccess,BOOL bInherithandle,PCTSTR psz
26、Name);验证程序:生产者与消费者问题。#include stdafx.h#include #include #define THREAD_INSTANCE_NUMBER 3#define PRODUCT_NUMBER 2#define MAX_ITEMS 2/ 定义信号量HANDLE m_S_Full; / SemaphoreHANDLE m_S_Empty; / SemaphoreHANDLE m_E_Mutex; / Eventint counter=0;void ThreadProducer(void *pData) /生产者int j;int ThreadNumTemp=(*(in
27、t *) pData); for (j=0;jPRODUCT_NUMBER;j+) WaitForSingleObject(m_S_Empty, INFINITE);WaitForSingleObject(m_E_Mutex, INFINITE);/ OK now, put product counter+;coutThreadProducer:ThreadNumTemp puts a porduct.endl;coutThreadProducer:ThreadNumTemp counter=counterendl; / relase comsumers semaphoreReleaseSem
28、aphore(m_S_Full, 1, NULL);/ set event to signal SetEvent(m_E_Mutex);void ThreadConsumer(void *pData) /消费者int j;int ThreadNumTemp=(*(int *) pData); for (j=0;jPRODUCT_NUMBER;j+) WaitForSingleObject(m_S_Full, INFINITE);WaitForSingleObject(m_E_Mutex, INFINITE);/ OK now, get a product counter-;coutThreadConsumer:ThreadNumTemp gets a porduct.endl;coutThreadConsumer:ThreadNumTemp counter=counterendl; / relase producers semaphoreReleaseSema
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 闸机系统施工方案
- 奉贤区拉森钢板桩施工方案
- 低碳绿色施工方案
- 酒店会议室墙布施工方案
- 建筑工地临时便道施工方案
- 中 关 村:威海市惠河路-90 号 7 幢工业房房地产抵押估价报告
- 恒鑫生活:公司财务报表及审阅报告(2024年1月-12月)
- 东鹏饮料(集团)股份有限公司2024年年度报告摘要
- 超级难的初三数学试卷
- 压井施工方案
- 高低压配电系统讲解通用课件
- 民事纠纷与民事诉讼课件
- 垂直细分领域分析报告
- 电气自动化专业单招高职2024年技能考试题目及答案
- 舞台彩绘妆面培训课件
- 人工智能行业教育与人才培养政策解读研讨会
- 【课件】问题研究+汽车工业能否带动家乡的发展高二地理人教版(2019)选择性必修2
- 体能训练预防训练伤
- 内分泌科护理常规的课件
- 疼痛科营销方案
- 中医药在关节病变治疗中的价值
评论
0/150
提交评论