Да, создпние lock не атамарно.
Идея с интерлоком
Но теоретически у нас может быть два static LONG volatile lock = sisNotInitialized, а следовательно два инстанса синглетона, один из которых "потеряеться", если два потока одновременно войдут в эту точку.
Шото вроде такого:
Код:enum SingletonInitState {sisNotInitialized = -1, sisInitializing = 0, sisInitialized = 1}; static LONG volatile lock = sisNotInitialized;
при запуске lock, в рассматриваемом случае, с наибольшей вероятностью будет равен 0 (т.е. sisInitializing), но компилятор этого не гарантирует, т.е. в зависимости от компилятора и ОС, там может быть либо ноль либо мусор. Полагаться на значение lock нельзя =)
Поэтому static переменную тут никак не получится использовать для синхронизации. Так что похоже, что кроме именованного объекта синхронизации ОС, других вариантов нет
при запуске lock, в рассматриваемом случае, с наибольшей вероятностью будет равен 0 (т.е. sisInitializing), но компилятор этого не гарантирует, т.е. в зависимости от компилятора и ОС, там может быть либо ноль либо мусор. Полагаться на значение lock нельзя =)
Как это нельзя, если стандарт х03 говорит о инициализации статических переменных области видимости метода не позднее момента первого вызова метода? )
Читайте стандарт )
static LONG volatile lock = sisNotInitialized;
if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized)
{
_instance = new Singleton();
lock = sisInitialized;
break;
}
Почему это еще? Она будет успешно проинициализирована до первого использования. Вот если бы ее вынесли в мембер класса - тогда да, возможны варианты.
Ребят, о чём вы пишете?)
Давайте асмовский разворот, ага?)
Или давайте проще: готовый пример, где этот код будет валиться)
если это происходит в одном потоке - совершенно верно, но у нас то задача для многопоточной среды.
Кстати, в твоем примере сразу две проблемы - первую (со статиком) я выше описал, а вторая - твой способ с интерлоком работать в многопоточной среде не будет. Т.к. инициализация lock не атомарна, как правильно заметил lugal.
Чтобы понять проблему - достаточно рассмотреть такую ситуацию: один поток вошол в Instance, но приостановился перед инструкцией записи в ячейку lock, на этой строке:
Код:static LONG volatile lock = sisNotInitialized;
Чтобы понят проблему, нужно открыть стандарт и понять, что инициализация происходит ДО!
я выше подробно разжевал где и как он будет валиться
Ну я вот олень, не понимаю с позиции того, что знаю. Давайте готовый пример рассмотрим с проблемами.
если не ошибаюсь, мы возможно в одной компании работаем, недавно был дев толк на эту тему, нужно было зайти послушать
В этот момент первый поток получает управление и завершает запись в lock = sisNotInitialized, далее первый поток тоже делает InterlockedCompareExchange и входит в инициализацию повторно (ведь он перед проверкой сам записал в lock значение sisNotInitialized)
Чтобы понят проблему, нужно открыть стандарт и понять, что инициализация происходит ДО!
Ну я вот олень, не понимаю с позиции того, что знаю. Давайте готовый пример рассмотрим с проблемами.
static LONG volatile lock = sisNotInitialized;
Нужно открыть стандарт и почитать внимательнее, если Instance вызывается ВО ВРЕМЯ инициализации, то никто не гарантирует что lock будет инициализирован ДО вызова Instance. Более того, проверка в MSVC от VS2010 показывает - Instance будет вызван ДО инициализации lock. Это одна проблема.
Спецификацией установлено, что статические локальные переменные инициализируются при первом выполнении блока кода, в котором они объявлены. При первом - если что, это значит не после )
C++’s abstract machine is single-threaded, and C++ compilers may choose to generate thread-unsafe code from source
Я всегда знал, что студийный компилер уёбищный )
Я всегда знал, что студийный компилер уёбищный )
Спецификацией установлено, что статические локальные переменные инициализируются при первом выполнении блока кода, в котором они объявлены. При первом - если что, это значит не после )
Другой вариант, что сама по себе инициализация многоходова и в теории действительно могут быть гонки. Но опять же, я не проверял.
static time_t Curtm = time(NULL);
001E13DE mov eax,dword ptr [$S1 (1E7140h)]
001E13E3 and eax,1
001E13E6 jne GetA+44h (1E1404h)
001E13E8 mov eax,dword ptr [$S1 (1E7140h)]
001E13ED or eax,1
001E13F0 mov dword ptr [$S1 (1E7140h)],eax
001E13F5 push 0
001E13F7 call time (1E1440h)
001E13FC add esp,4
001E13FF mov dword ptr [tm (1E7144h)],eax
return tm;
001E1404 mov eax,dword ptr [tm (1E7144h)]
DCL в С++ без volatile работает херовоВ дебаге будет все ок, а в релизе заебешься искать чего там компилер наоптимизировал.
Мутех конечно хорошо, но дорого.
Немного отошел от С++..
Посему вопрос:
volatile уже гарантирует Memory Barrier?