我有一个使用关键节的Windows DLL(C语言(。一个被调用多次的特定例程在第一次被调用时需要执行一些初始化代码,所以我使用的是Critical Section。然而,由于它被调用了很多次,我试图避免每次调用时进入该部分的开销。它似乎在工作,但我想知道在x64操作系统的多处理器(英特尔(系统上运行时,考虑到内存屏障/栅栏是否存在缺陷?这是精简的代码:
int _isInitialized = FALSE;
CRITICAL_SECTION _InitLock = {0};
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
ARM_SECTION_BEGIN(ul_reason_for_call)
switch (ul_reason_for_call)
{
case (DLL_PROCESS_ATTACH):
InitializeCriticalSection(&_InitLock);
break;
case (DLL_THREAD_ATTACH):
break;
case (DLL_THREAD_DETACH):
break;
case (DLL_PROCESS_DETACH):
DeleteCriticalSection(&_InitLock);
break;
}
return (TRUE);
}
int myproc(parameters...)
{
if (!_isInitialized) // check first time
{
EnterCriticalSection(&_InitLock);
if (_isInitialized) // check it again
{
LeaveCriticalSection(&_InitLock);
goto initialized;
}
... do stuff ...
_isInitialized = TRUE;
LeaveCriticalSection(&_InitLock);
}
initialized:
... do more stuff ...
return(something)
}
。。。如果考虑到记忆障碍/栅栏有缺陷?
使用volatile
在第二次测试中,代码显然有使用过时的_isInitialized
值的风险。
if (!_isInitialized) {
EnterCriticalSection(&_InitLock);
if (_isInitialized) // Risk
要确保重新读取_isInitialized
,请使用volatile
@JimmyB
// int _isInitialized = FALSE;
volatile int _isInitialized = FALSE;
其他共享数据
除了在... do stuff ...
中分配并在稍后的... do more stuff ...
代码中使用的_isInitialized
之外的其他数据由于优化可能在第一个if (!_isInitialized)
之前读取other_data
而冒着相同问题的风险。
代码可以使用volatile other_data
。不幸的是,这可能会导致无法接受的性能拖累。备选方案取决于stuff
内部的内容。
样式
我会将_isInitialized
设为函数的本地,删除_
并避免使用goto
。
int myproc(parameters...) {
static volatile int isInitialized = FALSE;
if (!isInitialized) {
EnterCriticalSection(&_InitLock);
if (!isInitialized) {
// ... do stuff ...
isInitialized = TRUE;
}
LeaveCriticalSection(&_InitLock);
}
// ... do more stuff ...
return(something)
}