[置顶] 线程的同步

使隶属于同一个进程下的各个线程协调一致的工作成为线程同步。MFC提供了多种同步对象,如CEvent、CCriticalSection、CSemephore、CMutex等。另外,MFC也提供了线程同步辅助类CSiingleLock和CMutiLock。通过这些类,可以比较容易的做到线程同步。
等待函数
Win32 API提供了一组能使线程阻塞其自身执行的等待函数。这些函数只有在作为其参数的一个或多个同步对象产生信号时才会返回。在超过规定的等待时间后,不管有无信号,函数也都会返回。在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的CPU时间。
使用等待函数即可以保证线程的同步,又可以提供程序的执行效率吧。其中最常用的等待函数有WaitForSingleObject和WaitForMultipleObject,他们分别用于检测单个同步对象和多个检测同步对象。
1.WaitForSingleObject
函数的原型:DWORD WaitForSingleObject(HANDLE hHandle,DWORD deWilliseconds);
参数hHandle是同步对象的句柄。参数dwMilliseconds是以毫秒为单位的超时间隔,如果该参数为0,那么函数就测试同步对象的状态并立即返回;如果该参数为INFINITE,则超时间隔是无限的。
2.WaitForMultipleObject
函数的原型:DWORD WaitForMultipleObject(DWORD nCount,CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD deMilliseconds);
lpHandles代表一个句柄数组,而参数nCount是句柄数组中句柄的数目。参数bWaitAll说明了等待类型,如果为TURE,那么函数在所有对象都有信号才会返回;如果是FLASE,则只要有一个对象变成有信号的函数就会返回。参数deMilliseconds是以毫秒为单位的超时间隔,如果该参数为0,那么函数就测试同步对象的状态并立即返回;如果该参数为INFINITE,则超时间隔是无限的。


CEvent类实现线程的同步
事件对象(CEvent)是最简单的同步对象,它包括了有信号和无信号的两种状态。在县城访问某一资源之前,也许需要等待某一事件的发生,这时用事件对象是最合适的。例如,只有在通信端口缓冲区收到数据后,监视线程才被激活。
MFC中,CEvent类提供了对事件的支持。CEvent对象有两种类型:人工事件和自动事件。对于自动事件,当其获得信号后,就会释放下一个可用的线程。一个自动CEvent对象在被至少一个线程释放后会自动返回到无信号的状态;而人工事件对象获得信号后,释放所有可利用线程,直到调用成员函数ReSetEvent()将其设置为无信号状态为止(再创将CEvent类的对象时候,默认的创建是自动事件)。
一个CEvent对象在线城中被创建后,自动处于无信号的状态,但在另一个线程中可以调用WaitForSingleObject来监视其状态。


CCriticalSection类实现线程同步
当多个线程访问一个独占性共享资源时,可以使用Critical Section(临界区)对象。任意时刻只有一个线程可以拥有临界区对象,拥有临界区对象的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程被挂起等待,知道拥有临界区对象的线程放弃临界区对象为止。因此,任意时刻只有一个线程可以拥有临界区对象,而只有拥有临界区对象的线程才可以访问受保护的数据。
使用CCriticalSection有两种方法:
其一:1.单独使用CCriticalSection类的一个全局对象,以使各个线程均能访问,例如:CCriticalSection critical_section(CCriticalSection类的构造函数是由一种形式,及不带任何的参数)
2.在访问需要保护的资源或代码之前,调用CCriticalSection类的成员函数Lock活的临界区对象。如critical_section.Lock();如果此时没有去他线程占用临界区对象,则调用Lock函数的现成活的临界区;否则线程即将挂起,并放入到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。
3.访问临界区完毕后,使用CCriticalSection的成员函数Unlock来释放临界区。代码如:critical_section.Unlick();
其二:1.与同步辅助类CSingleLock或者CMutiLock一起使用
2.定义CCriticalSection类的一个全局对象,格式如:CCriticalSection critical_section;
3.在访问需要保护的资源之前,定义CSingleLock类的一个变量,并将critical_Section的地址传送给构造函数:CSingleLock singlelock(&critical_section);
4.使用CStingleLock类的成员函数Lock请求活的临界区。如:singlelock.Lock();如果临界区已经被其他线程占用,则本线程挂起,等待临界区被释放。活的临界区对象后返回。
5.本线程中访问临界区中的共享资源后,调用CSingleLock类的成员函数Unlock来释放临界区对象,如singlelock.Unlock();


互斥对象(Mutex)类似于临界区对象,但与临界区对象的不同点在于互斥对象可以在进程间使用,而临界区对象只能在同一进程的各线程间使用。当然互斥对象也可以用于同一进程的各个线程间,但是在这种情况下使用临界区对象会更节省系统资源,更有效率。


CSemaphore类实现线程同步

使用信号量对象(Semaphore)也可以实现线程同步。信号量对象维护一个从0开始的计数,在计数值大于0时对象是有信号的,而在计数值为0是无信号的。通过使用信号量对象,可以限制对共享资源进行访问的线程数量。

MFC中,CSemaphore类实现了对信号量对象的封装。具体来讲CSemaphore的一个对象保存了对当前访问某一指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程的数目。如果这个计数达到了零,则所有对这个CSemaphore类对象锁孔哈I找的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零时为止。

当一个线程访问了被保护的资源是,计数值减1;一个县城完成了对被控共享资源的访问时,计数值增1;

在CSemaphore类对象的构造函数中可以指定控制的资源可以同时接受访问的最大线程数,构造函数原型:CSemaphore(LONG lInitialCount = 1,LONG lMaxCount =1,LPCTSTR pstrName = NULL,LPSECURITY_ATTRIBUTES lpaxAttributes = NULL);

CSemaphore类一般也与线程同步辅助类CSingleLock或者CMutiLock类结合起来。其用法与CCriticalSection类似。

你可能感兴趣的:(线程的同步,CSemaphore,CEvent)