![]() ![]() |
|
Win32下两种用于C++的线程同步类(下) | |
作者:佚名 文章来源:不详 点击数 更新时间:2008/3/7 12:10:47 文章录入:杜斌 责任编辑:杜斌 | |
|
|
总体的结构与上一篇中的类似,都是写出一个对锁进行封装的基类,再写一个用于调用加、解锁函数的类,通过对第二个类的生命周期的管理实现加锁和解锁。这里涉及到两个新问题,一是加锁、解锁动作都有两种,一种是加/解读锁,一种是加/解写锁;二是为了允许读-读并发,这里只声明一个Mutex是不够的,必须要声明多个Mutex,而且有多少个Mutex就同时允许多少个读线程并发,之所以这么说,是因为我们要使用的API函数是WaitForMultipleObjects。 WaitForMultipleObjects函数的功能就是等待对象状态被设置,MSDN中对它的说明为: Waits until one or all of the specified objects are in the signaled state or the time-out interval elapses. 这是个很好用的函数,我们可以用它来等待某个或某几个对象,并且允许设置超时时间,等待成功时与超时时返回的值是不同的。如果返回的值比WAIT_ABANDONED小则表示等待成功。“等待成功”对于不同类型的内核对象有不同的意义,例如对于进程或线程对象,等待成功就表示进程或线程执行结束了;对于互斥量对象,则表示此对象现在不被任何其他线程拥有,并且一旦等待成功,当前线程即拥有了此互斥量,其他线程则不能同时拥有,直接调用ReleaseMutex函数主动释放互斥量。 与WaitForMultipleObjects类似的还有一个函数WaitForSingleObject,它的功能比较简单,只针对单一个对象,而WaitForMultipleObjects可以同时等待多个对象,并且可以设置是否等待所有对象。 上一篇文章中用的InstanceLockBase类里面封装了一个Critical Section对象,这里则要封装一组Mutex的Handle,那么这一组是多少个呢?它应该由使用此类的程序中定义,例如可以用动态数组的方法: //基类: class RWLockBase //表示Read/Write Lock ...{ HANDLE* handles; protected: RWLockBase(int handleCount) ...{ handles = new HANDLE[handleCount]; } … }; //子类: class MyClass: public RWLockBase ...{ MyClass(): RWLockBase(3) ...{} … }; 这确实是个不错的办法,通过在子类构造函数的初始化段中调用基类构造函数并传参,使得这个动态数组得以正确初始化,不过这样看着不太爽,子类必须两次出现“RWLockBase”一词,能不能像InstanceLockBase那样只要继承了就好呢?答案是肯定的,只要用C++模板即可: template <int maxReadCount> class RWLockBase ...{ HANDLE handles[maxReadCount]; … }; 使用模板附带这么一个好处,因为模板参数是在编译期可以确定的,所以无需再用动态数组,直接在栈上分配即可。而使用模板引出一个新问题,就是相应的Lock类(RWLock)在构造时传的对象指针时的类型声明,直接写成RWLock(RWLockBase* pObj)肯定是不行的,因为必须指定模板参数,并且其值还必须与声明RWLockBase时所指定的值一致才行,从而客户端代码就必须两次指定模板参数值,不爽!解决的办法也是有一个,就是把RWLockBase变成夹层类,为它再声明一个基类,让RWLock接收的是基类指针,并把Lock、Unlock等函数放在基类中,声明为纯虚函数,实现写在夹层类中: class _RWLockBase ...{ friend class RWLock; protected: virtual DWORD ReadLock(int timeout) = 0; virtual void ReadUnlock(int handleIndex) = 0; virtual DWORD WriteLock(int timeout) = 0; virtual void WriteUnlock() = 0; }; |
|
![]() ![]() |