我想避免多次从进程中的线程调用RegisterClassEx()。为此,我修改了现有功能,如下所示。
目前的代码结构在使用替代方法实现线程安全方面存在约束,因此现在我试图坚持尽可能少的更改。
请让我知道您对以下代码的看法。
foo()
{
static ATOM atom = 0;
if( atom == 0 )
{
{
EnterCriticalSection(&m_CSRegisterClassEx);
if( atom == 0 )
{
atom = RegisterClassEx(&tCls);
if( atom == 0)
{
ERROR(L"RegisterClassEx failed! );
LeaveCriticalSection(&m_CSRegisterClassEx);
return 0;
}
else
{
ERROR(L"RegisterClassEx good!");
LeaveCriticalSection(&m_CSRegisterClassEx);
return atom;
}
}
}
}
else
{
ERROR(L"using atom[%ld] from last call!", atom);
return atom;
}
}
在这里输入代码
答案 0 :(得分:0)
双重检查模式不是线程安全的,请查看本文http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
这是一个解决方案,类似于Qt的Q_GLOBAL_STATIC实现和chrome的符号实现。
template<class T>
class TwStaticObject
{
public:
TwStaticObject(void)
: p(nullptr)
, x(0)
{
}
~TwStaticObject(void)
{
}
struct Deleter
{
Deleter(TwStaticObject& This)
: __this(This)
{
;
}
~Deleter()
{
if (__this.p)
{
delete __this.p;
__this.p = 0;
__this.x = 0;
}
}
TwStaticObject& __this;
};
static T* singletonInstance(TwStaticObject& thisobj)
{
if (InterlockedCompareExchange(&thisobj.x, 1, 0) == 0)
{
static T* obj = new T;
InterlockedExchangePointer((volatile PVOID *)&thisobj.p, (PVOID)obj);
static Deleter ThisDelter(thisobj);
}
else
{
while (thisobj.p == nullptr)
{
Sleep(0);
}
}
return thisobj.p;
}
T* volatile p ;
volatile long x ;
};
#define TwDefine_SingleTon(Type, FUN) \
static TwStaticObject<Type> ThisSingleTon##Type##FUN;\
static Type* FUN()\
{\
return TwStaticObject<Type>::singletonInstance(ThisSingleTon##Type##FUN);\
}
比,注册:
class Register{
public:
Register(){
atom = RegisterClassEx(&tCls);
}
ATOM atom;
};
TwDefine_Static(Register, _register);
在需要时调用_register()。
答案 1 :(得分:-1)
这是经典的双重检查锁定。这里的主要问题如下: 让我们说,第一个线程第一次进入函数。检查atom为0,锁定临界区并开始初始化。它将atom设置为非0,并且此更改将反映在主内存中(因为它可以!)。但是,RegisterClass调用的结果不反映在主内存中,并保留在CPU缓存中。之后,CPU决定它过热并进入休眠状态。
在这个完美的时间,另一个线程在不同的CPU上进入该功能。它认为ATOM是非空的,并且很高兴地返回它 - 但无论谁想要使用原子都会让人大吃一惊!
修改强>
刚刚注意到有问题的编译器。由于VS2010不支持线程安全的静态变量,因此这里还存在另一个问题。原子可能会重新初始化为0秒。这可能发生在以下情形中:
线程一进入该功能,检查隐藏的&#39; inited&#39;与atom相关联的值,发现它为false,将atom设置为0,inited为true,进入临界区并注册class - 此时它将进入休眠状态。第二个线程进入,检查初始值,发现它是假的 - 因为它没有阅读围栏,所以它可以读取陈旧值 - 并将原子设置为0.需要我继续?
明显的解决方案(不是最好的解决方案,但考虑到约束条件)是在定义静态之前进入关键部分。