Критические разделы и ограждения памяти / барьеры в многопроцессорной системе - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть Windows DLL (в C), которая использует критические разделы.Определенная процедура, которая вызывается много раз, должна выполнять некоторый код инициализации при первом вызове, поэтому я использую критический раздел.Однако, так как он вызывается так много раз, я стараюсь избегать лишних затрат на вход в раздел каждый раз, когда он вызывается.Кажется, это работает, но мне интересно, есть ли недостаток с учетом барьеров / ограждений памяти при работе в многопроцессорной (Intel) системе с ОС 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)
}

1 Ответ

0 голосов
/ 04 декабря 2018

... если есть недостатки с учетом барьеров / ограждений памяти ...?

Использовать volatile

Код, безусловно, рискует использовать устаревшее значение для _isInitialized во 2-м тесте.

if (!_isInitialized) {
  EnterCriticalSection(&_InitLock);
  if (_isInitialized)                 // Risk

Чтобы обеспечить перечитывание _isInitialized, используйте volatile. @ JimmyB

// int _isInitialized = FALSE;
volatile int _isInitialized = FALSE;

Другие общие данные

Другие данные, кроме _isInitialized, назначенные в ... do stuff ... и используемые в более поздних *Код 1029 * рискует той же проблемой, поскольку оптимизация может читать other_data перед первым if (!_isInitialized).

Код может использовать 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)
}
...