CEvent,CSemaphore,CCriticalSection,CMutex
一、用CEvent实现线程同步
事件对象(Event)是最简单的同步对象,它包括有信号和无信号两种状态。在线程访问某一资源之前,也许需要等待某一事件的发生,这时用事件对象最合适。例如,只有在通信端口缓冲区收到数据后,监视线程才被激活。MFC中,CEvent类提供了对事件的支持。CEvent对象有两种类型:人工事件和自动事件。对于自动事件,当其获得信号后,就会释放下一个可用的线程。一个自动CEvent对象在被至少一个线程释放后会自动返回到无信号状态;而人工事件对象获得信号后,释放所有可利用线程,直到调用成员函数ReSetEvent()将其设置为无信号状态时为止。
在创建CEvent对象时,默认创建的是自动事件
CEvent类的构造函数原型如下:
CEvent(BOOL bInitia11y0wn=FALSE,//若bInitiallyOwn为TRUE,则使CMultilock类对象和CSingleLock类对象的线程可用;否则,要访问资源的线程必须等待。该参数的默认值为FALSEo
BOOL bManualReset = FALSE,//指定要创建的CEvent对象是属于手工事件还是自动事件。为TRUE,则为手工事件;否则为自动事件。该参数默认值为FALSE o
LPCTSTR lpszName=NULL,//指定要创建的事件对象的名,如果该事件对象将跨进程使用,则此参数不能为NULL。如果该参数和一个已经存在的CEvent对象相同,则该构造函数返回一个对这个已存在对象的引用;如果参数和一个已存在的非CEvent类的同步对象(如CMutex )相同,则对象创建失败。
LPSECURITY ATTRIBUTES lpsaAttribute=NULL)//指定要创建对象的安全属性,一般置为NULL o
在事件对象建成后,可以调用其成员函数来改变其状态。CEvent类的几个重要的成员函数
SetEvent:将CEvent类对象的状态设置为有信号状态,并且释放所有等待的线程;如果该事件是人工事件,则CEvent类对象保持为有信号状态,直到调用成员函数ResetEvent将其重新设为无信号状态时为止,这样该事件就可以释放多个线程;如果CEvent类对象为自动事件,则在SetEvent将事件设置为有信号状态后,CEvent类对象由系统自动重置为无信号状态,除非一个线程被释放
ResetEvent:该函数将事件的状态设置为无信号状态,并保持该状态直至SetEvent被调用时为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数
PulseEnent:发送一个事件脉冲,该函数完成一系列操作后才返回。对于自动事件,PulseEvent将事件设置为有信号状态,等待一个线程被释放,将事件重置为无信号状态,然后PulseEvent返回;对于人工事件,则将等待该事件的所有线程被释放,事件被自动重置为无信号状态,然后PulseEvent返回
一个CEvent对象在线程中被创建后,自动处于无信号状态,但在另一个线程中可以调用API函数WaitForSingleObject来监视其状态
二、CCriticalSection类实现线程同步
当多个线程访问一个独占性共享资源时,可以使用Critical Section(临界区)对象。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止。因此,任一时刻,只有一个线程可以拥有临界区对象,而只有拥有临界区对象的线程才可以访问受保护的数据。
MFC的CCriticalSection类提供了对临界区对象的支持,其用法也相当简单,有两种用法。
1.单独使用CCriticalSection对象
1)定义CCriticalSection类的一个全局对象,以使各个线程均能访问,例如:CCriticalSection criticalsection;//CCriticalSection类的构造函数只有一种形式,即不带任何参数
2)在访问需要保护的资源或代码之前,调用CCriticalSection类的成员函数Lock获得临界区对象。代码如下:critical section.Lock();
如果此时没有其他线程占有临界区对象,则调用Lock函数的线程获得临界区;否则,线程即将挂起,并放人到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。
3)访问临界区完毕后,使用CCriticalSection的成员函数Unlock来释放临界区。代码如下:critical section.Unlock()
2.与同步辅助类CSingleLock或CMutiLock一起使用
下面以类CSingleLock为例,简单说明使用步骤:
1)定义CCriticalSection类的一个全局对象,格式如下:CCriticalSection critical_section;
2)在访问需要保护的资源之前,定义CSingleLock类的一个变量,并将critical_ section的地址传送给构造函数:CSingleLock singlelock(&critical_ section):
3)使用CSingleLock类的成员函数LOCk请求获得临界区。代码如下:singlelock.Lock();如果临界区已经被其他线程占用,则本线程挂起,等待临界区被释放。获得临界区对象后返回。
4)本线程中访问临界区中的共享资源后,调用CSingleLock类的成员函数Unlock来释放临界区:singlelock.Unlock();
三、CSemaphore类实现线程同步
使用信号量对象(Semaphore)也可以实现线程同步。信号量对象维护一个从0开始的计数,在计数值大于0时对象是有信号的,而在计数值为0时则是无信号的。通过使用信号量对象,可以限制对共享资源进行访问的线程数量。MFC中,CSemaphore类实现了对信号量对象的封装。具体来讲,CSemaphore的一个对象保存了对当前访问某一指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程的数目。如果这个计数达到了零,则所有对这个CSemaphore类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零时为止。当一个线程访问了被保护的资源时,计数值减1;一个线程完成了对被控共享资源的访问时,计数值增to在CSemaphore类对象的构造函数中可以指定控制的资源可以同时接受访问的最大线程数,
构造函数原型如下。
CSemaphore
(LONG lInitialCount=1,//信号量对象的初始计数值,决定了在信号量对象建成后,能同时访问其中资源的最大线程数目。必须不小于0,不大于lMaxCount
LONG lMaxCount=1,//信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目
LPCTSTR pstrName=NULL,//指向要创建的信号量名字。如果该信号量跨进程使用,则该参数不能为空
LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);
CSemaphore类一般也与线程同步辅助类CSingleLock或CMutiLock类结合使用。用法与CCriticalSection类似