OSMutex/OSCond Class

来源:互联网 发布:手机迅雷提示无网络 编辑:程序博客网 时间:2024/06/11 09:44

在有多个线程并发运行的环境中,能同步不同线程的活动是很重要的,DSS开发了OSMutex和OSCond两个类用以封装不同*作系统对线程同步支持的差异。

我们首先分析OSMutex类,这个类定义了广义互斥量的基本*作,类定义如下:

class OSMutex

{

1 public:

2 OSMutex(); //构造函数

3 ~OSMutex(); //析构函数

4 inline void Lock(); //加锁

5 inline void Unlock(); //解锁

6 inline Bool16 TryLock(); //异步锁,无论是否成功立即返回

7 private:

8 #ifdef __Win32__

9 CRITICAL_SECTION fMutex; //临界区

10 DWORD fHolder; //拥有临界区的线程id

11 UInt32 fHolderCount; //进入临界区线程数

//其他略…

}

在 Windows平台上,OSMutex类是通过临界区(CRITICAL_SECTION)来实现的,第10行定义了临界区变量fMutex。类实例化时 构造函数调用InitializeCriticalSection(&fMutex)初始化临界区变量,对应的在析构函数中调用 DeleteCriticalSection(&fMutex)清除。

Lock()函数用于对互斥量加锁,它调用私有方法RecursiveLock实现:

void OSMutex::RecursiveLock()

{

// 当前线程已经拥有互斥量,只需增加引用计数

1 if (OSThread::GetCurrentThreadID() == fHolder)

2 {

3 fHolderCount++; //增加引用计数

4 return;

5 }

6 #ifdef __Win32__

7 ::EnterCriticalSection(&fMutex); //申请进入临界区

8 #else

9 (void)pthread_mutex_lock(&fMutex);

10 #endif

11 Assert(fHolder == 0);

12 fHolder = OSThread::GetCurrentThreadID(); //更新临界区拥有者标志

13 fHolderCount++;

14 Assert(fHolderCount == 1);

}

第1行检测如果当前线程已经拥有互斥量,就只需将内部计数fHolderCount加1,以便纪录正在使用互斥量的方法数。如果当前线程还没有得到互斥 量,第7行调用EnterCriticalSection()函数申请进入临界区;如果当前已经有其他线程进入临界区,该函数就会阻塞,使得当前线程进入 睡眠状态,直到占用临界区的线程调用LeaveCriticalSection(&fMutex)离开临界区后才可能被唤醒。一旦线程进入临界区 后,它将首先更新临界区持有者标志(第12行),同时将临界区引用计数加1。

注意到另外一个函数TryLock(),该函数也是用于为互斥量加锁,但与Lock()不同的是,TryLock()函数为用户提供了异步调用互斥量的功 能,这是因为它调用::TryEnterCriticalSection(&fMutex)函数申请进入缓冲区:如果临界区没有被任何线程拥有, 该函数将临界区的访问区给予调用的线程,并返回TRUE,否则它将立刻返回FALSE。TryEnterCriticalSection()和 EnterCriticalSection()函数的本质区别在于前者从不挂起线程。

接着分析OSCond类,该类定义了状态变量(Condition Variable)的基本*作,类定义如下:

class OSCond

{

1 public:

2 OSCond(); //构造函数

3 ~OSCond(); //析构函数

4 inline void Signal(); //传信函数

5 inline void Wait(OSMutex* inMutex, SInt32 inTimeoutInMilSecs = 0);//释放锁inMutex,在等待到信号后,再重新给inMutex加锁

//等待传信函数

6 inline void Broadcast(); //广播传信函数

7 private:

8 #ifdef __Win32__

9 HANDLE fCondition; //事件句柄

10 UInt32 fWaitCount; //等待传信用户数

//其他略…

}

虽然同是用于线程同步,但OSCond类与OSMutex大不相同,后者用来控制对关键数据的访问,而前者则通过发信号表示某一*作已经完成。在 Windows平台中,OSCond是通过事件(event)来实现的;构造函数调用CreateEvent()函数初始化事件句柄 fCondition,而析构函数则调用CloseHandle()关闭句柄。

OSCond的使用流程是这样的:线程调用Wait(OSMutex* inMutex, SInt32 inTimeoutInMilSecs = 0)函数等待某个事件的发生,其中inTimeoutInMilSecs是最长等待时间,0代表无限长。Wait()函数内部调用了 WaitForSingleObject (fCondition, theTimeout)函数,该函数告诉系统线程在等待由事件句柄fCondition标识的内核对象变为有信号,参数theTimeout告诉系统线程 最长愿意等待多少毫秒。如果指定的内核对象在规定时间内没有变为有信号,系统就会唤醒该线程,让它继续执行。而函数Signal()正是用来使事件句柄 fCondition有信号的。Signal()函数内部实现很简单,只是简单调用SetEvent函数将事件句柄设置为有信号状态。

使用OSCond的过程中存在一种需求,就是希望通知所有正在等待的用户事件已经完成,而Signal()函数每次只能通知一个用户,因此又开发了另外一个广播传信函数如下:

inline void OSCond::Broadcast()

{ //提示:本函数相当循环调用Signal()函数

1 #ifdef __Win32__

2 UInt32 waitCount = fWaitCount; //等待传信的用户数

3 for (UInt32 x = 0; x < waitCount; x++) //循环为每个用户传信

4 {

5 BOOL theErr = ::SetEvent(fCondition); //设置事件句柄为有信号状态

6 Assert(theErr == TRUE);

7 }

//此处略…

}

Broadcast首先统计所有等待传信的用户数(第2行),然后用一个循环为每个用户传信(第3~7)行。这种编程方法虽然不是很优雅(elegant),但是由于Windows平台上不支持广播传信功能(Linux和Solaris均支持),也只好如此。

0 0
原创粉丝点击