class MutexLock
{
public:
MutexLock()
{
InitializeCriticalSection(&mutex_);
}
~MutexLock()
{
DeleteCriticalSection(&mutex_);
}
void lock()
{
EnterCriticalSection(&mutex_);
}
void unlock()
{
LeaveCriticalSection(&mutex_);
}
private:
MutexLock(const MutexLock&);
MutexLock& operator=(const MutexLock&);
CRITICAL_SECTION mutex_;
};
class MutexLockGuard
{
public:
explicit MutexLockGuard(MutexLock& m)
: mutex_(m)
{
mutex_.lock();
}
~MutexLockGuard()
{
mutex_.unlock();
}
private:
MutexLockGuard(const MutexLockGuard&);
MutexLockGuard& operator=(const MutexLockGuard&);
MutexLock& mutex_;
};
上面的两个类是RAII的体现,以对象管理资源。
需求:
现在要写一个log模块,要考虑到多个线程同时写log的互斥访问。所以定义下面的写日志类:
class WriteLog
{
public:
WriteLog(bool safe)
{
lock_= (safe ? (new MutexLock): 0);
}
//考虑线程安全时的写log操作函数
void write(const char* msg)
{
/*
此处的目的很明显:如果启用线程安全,则等待临界区
如果不使用线程安全,则直接进行写log操作(writeUnSafe)
*/
if(lock)
MutexLockGuard mg(lock);
writeUnSafe(...);
}
private:
MutexLock* lock_;
//这是不考虑线程安全时的写log操作函数
void writeUnSafe(.....);
}
write函数看起来很干净简洁,但是实际一运行,却发现,互斥不起作用,log文件被写成了一坨。调了好久,才发现,问题出在这里:
if(lock)
MutexLockGuard mg(lock);
writeUnSafe(...);
这几行代码相当于:
if(lock)
{
MutexLockGuard mg(lock);
}
writeUnSafe(...);
当mg离开if的右括号时,生命已经到头了,然后执行了mg的析构函数,释放了临界区。
这相当于脱了裤子放屁,不仅没有线程安全,还多了冗余的操作。
所以,还是老老实实写成这样:
if(lock)
{
MutexLockGuard mg(lock);
writeUnSafe(...);
}
else
writeUnSafe(...);