如果想要某些事情成对出现,使用构造函数和析构函数。
为保证线程同步/互斥,经常使用信号量、互斥量、临界区和事件。以临界区 CRITICAL_SECTION 为例,需要
InitializeCriticalSection 和 DeleteCriticalSection
EnterCriticalSection 和 LeaveCriticalSection
成对配合使用,否则可能出现资源泄漏甚至死锁的情况。
考虑以下程序片段,中间的代码可能出现异常,导致控制转移。或者函数有多个出口,就需要在每个出口前调用LeaveCriticalSection。
CRITICAL_SECTION _cs; void SomeFunc() { EnterCriticalSection(&_cs); //可能抛出异常 LeaveCriticalSection(&_cs); }
我们希望EnterCriticalSection(或者其他lock 函数)和 LeaveCriticalSection(或者其他unlock 函数)成对出现,最好的方法是用class来包装,且分别在构造函数和析构函数中实现。
#include <Windows.h> class Lock { public: Lock(); ~Lock(); void Enter(int id = 0); void Leave(); private: CRITICAL_SECTION _cs; DWORD dwOwningThread; int iEnterCount; int iId; }; class AutoLock { public: AutoLock(Lock* target, int id = 0); ~AutoLock(); private: Lock* _target; };
#include "AutoLock.h" Lock::Lock(): dwOwningThread(0), iEnterCount(0), iId(0) { InitializeCriticalSection(&_cs); } Lock::~Lock() { DeleteCriticalSection(&_cs); } void Lock::Enter(int id) { DWORD currentThread = GetCurrentThreadId(); EnterCriticalSection(&_cs); dwOwningThread = currentThread; iEnterCount++; iId = id; } void Lock::Leave() { if (--iEnterCount == 0) { dwOwningThread = 0; iId = 0; } LeaveCriticalSection(&_cs); } AutoLock::AutoLock(Lock* target, int id) : _target(target) { _target->Enter(id); } AutoLock::~AutoLock() { _target->Leave(); }
直接使用 AutoLock 类,而不用考虑释放临界区的问题。
Lock* workBufferLock; //... AutoLock lock(workBufferLock); //...
这个方法应用在很多场合,比如网络访问中建立连接和断开连接,数据库访问中的登录和登出,测量函数平均运行耗时等。
【参考】
Solmyr 的小品文系列之六:成对出现