Многопоточность C++ — потокобезопасный код

Я работаю над игровым движком, который работает с .dll. Внутри есть экспортированная функция, которая возвращает ссылку на объявленный в ней статический класс, как показано ниже:

__forceinline __declspec(dllexport) STATE* NF3DGetEngineState(void)
{
    static STATE s_State;
    return &s_State;
}

где STATE — это класс, который управляет всеми компонентами и имеет функции, которые обращаются к ним через критическую секцию:

void Set(int val)
{
    EnterCriticalSection(&CriticalSection);
    ClassMember = val;
    LeaveCriticalSection(&CriticalSection);
}

где «CriticalSection» — это CRITICAL_SECTION член класса STATE, который, конечно же, инициализирован. Контекст, в котором я использую эти функции:

NF3DGetEngineState()->Set(10);

Вопрос в том, является ли этот код потокобезопасным?

Из того, что я узнал, возврат ссылок на статические объявления не является потокобезопасным.

Что я могу сделать, чтобы это было так?


person featherless biped    schedule 23.10.2015    source источник
comment
Пожалуйста, объясните, почему вы думаете, что это не потокобезопасно   -  person Sami Kuhmonen    schedule 23.10.2015
comment
Я слышал, что возврат ссылок на статические объявления не является потокобезопасным? Я прав?   -  person featherless biped    schedule 23.10.2015
comment
Что ты делаешь с ClassMember? При настройке необходимо использовать ту же защиту ресурсов.   -  person JeffRSon    schedule 23.10.2015
comment
Я возвращаю его в других функциях типа get, которые точно так же содержат механизм снятия блокировки критической секции. Разница в том, что EnterCriticalSection и LeaveCriticalSection вызываются в конструкторе и деструкторе класса-оболочки, который всегда объявляется в этой функции.   -  person featherless biped    schedule 23.10.2015
comment
Первый вызов NF3DGetEngineState() может быть не потокобезопасным, в зависимости от компилятора. Но если вы вызовете его один раз при основном запуске (до того, как несколько потоков попытаются получить к нему доступ), после этого параллельные вызовы получат тот же экземпляр.   -  person birdypme    schedule 23.10.2015


Ответы (1)


Какая у вас версия С++? Если это С++ 11 или более поздняя версия, то код максимально безопасен для потоков. Если это pre-11, это небезопасно в отношении первого вызова NF3DGetEngineState.

Уточнение.

Дело не в том, что возврат ссылок на статические переменные «не является потокобезопасным». Наоборот, это 100% безопасно. Что не является потокобезопасным до C++11, так это сама инициализация статической переменной. pre-C++11 не дает никаких гарантий в отношении одновременных вызовов функции в первый раз. Фактически, все компиляторы C++ до 11, с которыми я работал, столкнулись бы с проблемой, если бы вы одновременно вводили эту функцию в первый раз. Причина в том, что код, который генерирует компилятор при использовании статических переменных, выглядит примерно так:

static bool static_var_initialized = false; 
if (!static_var_initialized) {
    new (&static_var) StaticVarType(); // Explicit constructor call
    static_var_initialized = true;
}

Очевидно, что существует возможность вызова конструктора несколько раз, если вам случится вызывать эту функцию несколько раз до того, как для статической переменной будет установлено значение true.

В C++11 есть гарантия, что этого никогда не произойдет, и конструктор будет вызываться только один раз. Это также гарантирует, что ни один поток не увидит несконструированное значение.

person SergeyA    schedule 23.10.2015
comment
Это то, что я искал. Спасибо за столь необходимую помощь! :) - person featherless biped; 23.10.2015