操作系统课件:Windows线程同步和互斥_第1页
操作系统课件:Windows线程同步和互斥_第2页
操作系统课件:Windows线程同步和互斥_第3页
操作系统课件:Windows线程同步和互斥_第4页
操作系统课件:Windows线程同步和互斥_第5页
已阅读5页,还剩29页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Windows线程同步和互斥Windows线程同步机制事件(Event)临界区(CriticalSection)互斥量(Mutex)信号量(Semaphore)同步机制说明互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。如果只为了在进程内部用的话使用临界区会带来速度上的优势并能够减少资源占用量。互斥量是跨进程的,一旦被创建,就可以通过名字打开它。创建互斥量需要的资源更多。同步机制说明(续)互斥量,信号量和事件都可以跨进程来实现同步数据操作。互斥量:资源独占使用信号量:资源计数器事件事件对象可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。事件是WIN32中最灵活的线程间同步机制。事件存在两种状态:激发状态(SignaledorTrue)未激发状态(UnsignaledorFalse)事件事件可分为两类:手动设置: 这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。自动恢复: 一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。

事件-函数原型函数原型:HANDLECreateEvent(

LPSECURITY_ATTRIBUTESlpEventAttributes,

//SECURITY_ATTRIBUTES结构指针,可为NULL

BOOLbManualReset,

//手动/自动

//TRUE:在WaitForSingleObject后必须手动调用ResetEvent清除信号

//FALSE:在WaitForSingleObject后,系统自动清除事件信号

BOOLbInitialState,//初始状态

LPCTSTRlpName

//事件的名称

);事件-函数原型(续)使用“事件”机制应注意以下事项:如果跨进程访问事件,必须对事件命名,在对事件命名的时候,要注意不要与系统命名空间中的其它全局命名对象冲突;事件是否要自动恢复;事件的初始状态设置。由于event对象属于内核对象,故进程B可以调用OpenEvent函数通过对象的名字获得进程A中event对象的句柄,然后将这个句柄用于ResetEvent、SetEvent和WaitForMultipleObjects等函数中。此法可以实现一个进程的线程控制另一进程中线程的运行,例如:

HANDLEhEvent=OpenEvent(EVENT_ALL_ACCESS,true,"MyEvent");

ResetEvent(hEvent);事件-例子三个线程:主线程读线程写线程读线程必须在写线程操作结束后才能进行读;主线程必须等读和写线程结束后才能结束代码include"stdafx.h"#include<windows.h>#include<process.h>#include<iostream.h>#include<fstream.h>HANDLEevRead,evFinish;voidReadThread(LPVOID

param){

WaitForSingleObject(evRead,INFINITE);

cout<<"Reading"<<endl;

SetEvent(evFinish);}voidWriteThread(LPVOID

param){

cout<<"Writing"<<endl;

SetEvent(evRead);}代码(续)int

main(int

argc,char*argv[]){

evRead=CreateEvent(NULL,FALSE,FALSE,NULL);

evFinish=CreateEvent(NULL,FALSE,FALSE,NULL); _beginthread(ReadThread,0,NULL); _beginthread(WriteThread,0,NULL);

WaitForSingleObject(evFinish,INFINITE);

cout<<"End."<<endl; return0;}临界区临界区是保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

临界区(续)临界区包含两个操作原语:EnterCriticalSection()进入临界区LeaveCriticalSection()离开临界区

EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。否则,临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。

临界区—Win32API创建临界区

为了创建临界区,首先必须在进程中分配一个全局CRITICAL_SECTION数据结构:

CRITICAL_SECTIONgCriticalSection;使用临界区

使用临界区之前,必须调用InitializeCriticalSection函数初始化: VOIDInitializeCriticalSection(LPCRITICAL_SECTION

lpCriticalSection);进入临界区

调用EnterCriticalSection函数进入临界区:

VOIDEnterCriticalSection(LPCRITICAL_SECTION

lpCriticalSection);

离开临界区

调用LeaveCriticalSection函数退出了临界区:

VOIDLeaveCriticalSection(LPCRITICAL_SECTION

lpCriticalSection);

删除临界区 调用DeleteCriticalSection函数删除临界区:

VOIDDeleteCriticalSection(LPCRITICAL_SECTION

lpCriticalSection);临界区-一般用法……EnterCriticalSection(&gCriticalSection

);//dosomethingLeaveCriticalSection(&gCriticalSection);……临界区-注意事项关于临界区的使用,有下列注意点:每个共享资源使用一个CRITICAL_SECTION变量;不要长时间运行关键代码段,当一个关键代码段长时间运行时,其他线程就会进入等待状态,这会降低应用程序的运行性能;如果需要同时访问多个资源,则可能连续调用EnterCriticalSection;CriticalSection不是OS核心对象,如果进入临界区的线程"挂"了,将无法释放临界资源。这个缺点在Mutex中得到了弥补。

临界区-例子一个银行系统中两个线程执行取款操作,一个使用ATM机,另一个使用存折在柜台取款。如果不加于控制,会使得账户余额为负数。代码#include"stdafx.h"#include<windows.h>#include<process.h>#include<iostream.h>#include<fstream.h>inttotal=1000;HANDLEevFin[2];CRITICAL_SECTIONcs;voidWithDrawThread1(LPVOIDparam){

EnterCriticalSection(&cs); if((total-900)>=0){ total-=900;

cout<<"youwithdraw$900."<<endl; }else{

cout<<"Noenoughmoney!"<<endl; }

LeaveCriticalSection(&cs); SetEvent(evFin[0]);}代码(续)voidWithDrawThread2(LPVOIDparam){

EnterCriticalSection(&cs); if((total-700)>=0){ total-=700;

cout<<"youwithdraw$700."<<endl; }else{

cout<<"Noenoughmoney!"<<endl; }

LeaveCriticalSection(&cs); SetEvent(evFin[1]);}代码(续)int

main(int

argc,char*argv[]){ evFin[0]=CreateEvent(NULL,FALSE,FALSE,NULL); evFin[1]=CreateEvent(NULL,FALSE,FALSE,NULL);

InitializeCriticalSection(&cs); _beginthread(WithDrawThread1,0,NULL); _beginthread(WithDrawThread2,0,NULL); WaitForMultipleObjects(2,evFin,TRUE,INFINITE);

DeleteCriticalSection(&cs);

cout<<total<<endl;

return0;}互斥量互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限。由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。互斥量用CreateMutex函数创建互斥量:

HANDLECreateMutex(

LPSECURITY_ATTRIBUTESlpMutexAttributes,

//安全属性结构指针,可为NULL

BOOLbInitialOwner,

//是否占有该互斥量,TRUE:占有,FALSE:不占有

LPCTSTRlpName

//信号量的名称

);

其它API打开一个互斥量:OpenMutex函数:

HANDLEOpenMutex(DWORDfdwAccess,

//值为SYNCHRONIZE或MUTEX_ALL_ACCESS

BOOLfInherit,LPTSTRlpszName);释放一个互斥量:ReleaseMutex函数:

BOOLReleaseMutex(HANDLE

hMutex);

该函数将互斥量从无信号状态变到有信号状态。

互斥和临界区的比较互斥量-例子代码#include"stdafx.h"#include<windows.h>#include<iostream.h>#defineTHREAD_INSTANCE_NUMBER3LONGg_RessourceInUse=FALSE;LONGg_iCounter=0;voidThreadProc(void*pData){

int

ThreadNumTemp=(*(int*)pData); HANDLEhMutex;

cout<<"ThreadProc:"<<ThreadNumTemp<<"inRunning!"<<endl; if((hMutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"Mutex.Test"))==NULL)

cout<<"Openmutexerror!"<<endl;

WaitForSingleObject(hMutex,INFINITE);

cout<<"ThreadProc:"<<ThreadNumTemp<<"getsthemutex."<<endl;

ReleaseMutex(hMutex);

CloseHandle(hMutex);

cout<<"ThreadProc:"<<ThreadNumTemp<<"end."<<endl;}互斥量-例子代码(续)int

main(int

argc,char*argv[]){ DWORDID[THREAD_INSTANCE_NUMBER]; HANDLEh[THREAD_INSTANCE_NUMBER]; HANDLEhMutex;

inti;

if((hMutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"Mutex.Test"))==NULL){ if((hMutex=CreateMutex(NULL,FALSE,"Mutex.Test"))==NULL){

cout<<"CreateMutexerror!"<<endl; return0; } }

for(i=0;i<THREAD_INSTANCE_NUMBER;i++) {

h[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,(void*)&ID[i],0,&(ID[i])); if(h[i]==NULL){

cout<<"Createthreaderror!"<<endl; }else{

cout<<"Createthread"<<ID[i]<<endl; } }

WaitForMultipleObjects(THREAD_INSTANCE_NUMBER,h,true,INFINITE);

cout<<"Closethemutexhandle!"<<endl; return0;}信号量信号量是维护0到指定最大值之间的同步对象。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。

信号量-创建和撤销创建信号量:

HANDLECreateSemaphore(

PSECURITY_ATTRIBUTEpsa,

LONGlInitialCount,//开始时可供使用的资源数

LONGlMaximumCount,//最大资源数

PCTSTRpszName);撤销信号量:

BOOLWINAPIReleaseSemaphore(

HANDLEhSemaphore,

LONGlReleaseCount,//信号量的当前资源数增加lReleaseCount

LPLONGlpPreviousCount

);信号量-打开打开信号量

HANDLEOpenSemaphore(

DWORDfdwAccess,

BOOLbInherithandle,

PCTSTRpszName

);#include"stdafx.h"#include<windows.h>#include<iostream.h>#defineTHREAD_INSTANCE_NUMBER3#definePRODUCT_NUMBER2#defineMAX_ITEMS2//定义信号量HANDLEm_S_Full;//SemaphoreHANDLEm_S_Empty;//SemaphoreHANDLEm_E_Mutex;//Eventintcounter=0;voidThreadProducer(void*pData){

intj;

int

ThreadNumTemp=(*(int*)pData);for(j=0;j<PRODUCT_NUMBER;j++){

WaitForSingleObject(m_S_Full,INFINITE);

WaitForSingleObject(m_E_Mutex,INFINITE); //OKnow,putproduct counter++;

cout<<"ThreadProducer:"<<ThreadNumTemp<<"putsaporduct."<<endl;

cout<<"ThreadProducer:"<<ThreadNumTemp<<"counter="<<counter<<endl; //relase

comsumer’ssemaphore

ReleaseSemaphore(m_S_Empty,1,NULL); //seteventtosignal

SetEvent(m_E_Mutex); }}int

main(int

argc,char*argv[]){//假设仓库最多容纳MAX_ITEMS个物品,开始仓库为空

DWORDIDP[THREAD_INSTANCE_NUMBER]; DWORDIDC[THREAD_INSTANCE_NUMBER]; HANDLEhp[THREAD_INSTANCE_NUMBER]; HANDLEhc[THREAD_INSTANCE_NUMBER];

inti;

m_S_Full=CreateSemaphore(NULL,0,MAX_ITEMS,NULL);//初始计数为M

m_S_Empty=CreateSemaphore(NULL,MAX_ITEMS,MAX_ITEMS,NULL);//初始计数为0

m_E_Mutex=CreateEvent(NULL,FALSE,TRUE,NULL);//自动类型,初始状态为信号态信号量例子-生产者消费者问题

for(i=0;i<THREAD_INSTANCE_NUMBER;i++) {

hp[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProducer,(void*)&IDP[i],0,&(IDP[i]));

hc[i]=CreateThread(NULL,0,(LPTHREAD_START_RO

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论