在多线程的情况下,如果存在多个线程要使用同一个资源的情况时,则需要在线程之间进行协调(同步)才能使程序完成预定的工作,而不会出现灾难性的冲突。
MFC 提供的多线程类分为两类:同步对象(CSyncObject 、CSemaphore 、CMutex 、CCriticalSection 和 CEvent )和同步访问对象(CMultiLock 和 CSingleLock )。
当必须控制对资源的访问以确保资源的完整性时,使用同步类。同步访问类用于获取对这些资源的访问权。本主题介绍各个类的适用情况。
若要确定应使用的同步类,请询问以下一系列问题:
-
应用程序必须等到发生某事才能访问资源(例如,在将数据写入文件之前,必须先从通信端口接收它)吗?
如果是,请使用 CEvent 。 -
同一应用程序内一个以上的线程可以同时访问此资源(例如,应用程序允许在同一文档上最多同时打开五个带有视图的窗口)吗?
如果是,请使用 CSemaphore 。 -
可以有一个以上的应用程序使用此资源(例如,资源在 DLL 中)吗?
如果是,请使用 CMutex 。如果不是,请使用 CCriticalSection 。
从不直接使用 CSyncObject 。它是其他四个同步类的基类。
同步访问对象的作用,为了统一方便丰富的调用接口,并且当超过使用范围后,就会自动析构调用Unlock (),防止忘记解锁。
若为了使用同步类CSemaphore, CMutex, CCriticalSection和CEvent,可以创建CMultiLock或CSingleLock对象以等待或释放同步对象。若在某个特定的时间希望使用多个对象,就请使用CMultiLock 。否则,当仅仅在某时需等候某一对象时,请使用CSingleLock 。要使用一个CSingleLock 对象,在被控制资源的类中的一个成员函数内部调用CSingleLock 的构造函数。然后调用ISLock成员函数来确定这个资源是否可用。如果资源是可用的,则继续该成员函数的其余部分。如果资源不能使用,可以在一个指定的时间内等待资源被释放,或者是返回失败。在使用完资源后,如果CSingleLock 对象要被再次使用,可以调用Unlock函数,或者销毁CSingleLock 对象。
CSingleLock 对象需要有一个从CSyncObject 派生的对象存在。这通常是一个被控制资源的类的数据成员。若要使用CMultiLock 对象,首先要创建希望等待的同步对象的数组。然后,调用被控制的资源类成员函数内的CMultiLock对象的构造函数。其后调用Lock 成员函数来决定资源是否为有效资源(被标记)。若有,就继续进行该成员函数的操作。否则,要么等待一定的时间,等资源被释放,要么返回失败。在资源完全使用后,要么当再一次使用CMultiLock 对象调用Unlock 函数,要么允许销毁CMultiLock 对象。CMultiLock 对象在线程有大量响应的CEvent 对象时非常有用。首先创建一个包含所有CEvent 指针的数组,然后调用Lock 函数。这将导致线程等待到某个事件被标记。
使用CSingleLock 的示例:
// m_CritSection is a data member (of type CCriticalSection)
// of an existing class that implements the resource being shared.
// Relate the synchronization object (m_CritSection) with
// our CSingleLock object.
CSingleLock singleLock(&m_CritSection);
singleLock.Lock(); // Attempt to lock the shared resource
if (singleLock.IsLocked()) // Resource has been locked
{
//...use the shared resource...
// Now that we are finished,
// unlock the resource for others.
singleLock.Unlock();
}