谈单例模式的线程安全(一)
在生产者/消费者模式中,生产者和消费者都要访问同一个资源,这个时候就需要用单例模式保证只存在一份资源,同时又要保证访问的这个资源类是线程安全的,因为生产者和消费者不在同一个线程。
第一部分:资源的线程安全机制
第一版,先写一个资源类,用关键代码段保证线程安全:
class ImageQueue
{
public:
ImageQueue(){
InitializeCriticalSection(&cs);
}
~ImageQueue(){
DeleteCriticalSection(&cs);
}
void Add(const NameInfo& newImage){
EnterCriticalSection(&cs);
image_queue_.push(newImage);
LeaveCriticalSection(&cs);
}
bool Get(NameInfo& image){
EnterCriticalSection(&cs);
bool ret = image_queue_.empty();
if(!ret){
image = image_queue_.front();
image_queue_.pop();
}
LeaveCriticalSection(&cs);
return !ret; // 关键代码段不能保护return 代码行
}
protected:
std::queue<NameInfo> image_queue_;
CRITICAL_SECTION cs;
};
上述代码有个弊端,遇到return或break时候不能很好的控制,尤其函数体内不止一处return的时候。可以利用局部变量离开函数体自动销毁的特性免除这些顾虑。
封装一下CRITICAL_SECTION:
class CriticalSection
{
public:
CriticalSection() {InitializeCriticalSection(&cs_);}
~CriticalSection() {DeleteCriticalSection(&cs_);}
void Lock() {EnterCriticalSection(&cs_);}
void UnLock() {LeaveCriticalSection(&cs_);}
protected:
CRITICAL_SECTION cs_;
};
class CriticalSectionEx : protected CriticalSection
{
public:
CriticalSectionEx() {CriticalSection::Lock();}
~CriticalSectionEx() {CriticalSection::UnLock();}
};
使用CriticalSectionEx类就可以利用局部变量离开函数体自动销毁的特性解决上面的问题了,嘻嘻。
停!不能在每次需要保护的代码段都定义一个CRITICAL_SECTION,再初始化,销毁,这太浪费资源了。
于是,有了下面这个CriticalSection辅助类:
class CsAssist
{
public:
CsAssist(CriticalSection *cs) {pcs_ = cs; pcs_->Lock();}
~CsAssist() {pcs_->UnLock();}
private:
CriticalSection* pcs_;
};
CsAssist类在构造的时候避免了CRITICAL_SECTION的定义、初始化、销毁,前提是资源类自己必须提供一个CriticalSection对象。
第二版:改进后的线程安全机制(保证了return行受到保护,避免了CRITICAL_SECTION的频繁定义、初始化、销毁):
class ImageQueue
{
public:
void Add(const NameInfo& newImage){
CsAssist csas(&cs);
image_queue_.push(newImage);
}
bool Get(NameInfo& image){
CsAssist csas(&cs); // csas在函数体内构造,析构。
bool ret = image_queue_.empty();
if(!ret){
image = image_queue_.front();
image_queue_.pop();
}
return !ret;
}
protected:
std::queue<NameInfo> image_queue_;
CriticalSection cs;
};
摘自:HOHO的专栏
补充:综合编程 , 安全编程 ,