Czy poniższa implementacja Singleton
wątku (Meyers 'Singleton) przy użyciu leniwej inicjalizacji jest bezpieczna?
static Singleton& instance()
{
static Singleton s;
return s;
}
Jeśli nie, dlaczego i jak zabezpieczyć wątek?
Czy poniższa implementacja Singleton
wątku (Meyers 'Singleton) przy użyciu leniwej inicjalizacji jest bezpieczna?
static Singleton& instance()
{
static Singleton s;
return s;
}
Jeśli nie, dlaczego i jak zabezpieczyć wątek?
Odpowiedzi:
W C ++ 11 jest bezpieczny wątkowo. Zgodnie z normą ,
§6.7 [stmt.dcl] p4
:Obsługa GCC i VS dla tej funkcji ( Dynamic Initialization and Destruction with Concurrency , znana również jako Magic Statics w MSDN ) jest następująca:
Dzięki @Mankarse i @olen_gam za ich komentarze.
W C ++ 03 ten kod nie był bezpieczny dla wątków. Jest artykuł Meyersa zatytułowany „C ++ and the Perils of Double-Checked Locking”, który omawia bezpieczne wątkowo implementacje wzorca, a wniosek jest mniej więcej taki, że (w C ++ 03) pełne blokowanie wokół metody tworzenia instancji jest w zasadzie najprostszym sposobem zapewnienia właściwej współbieżności na wszystkich platformach, podczas gdy większość form podwójnie sprawdzonych wariantów wzorca blokowania może cierpieć z powodu warunków wyścigu na niektórych architekturach , chyba że instrukcje są przeplatane strategicznie rozmieszczonymi barierami pamięci.
źródło
Odpowiadając na pytanie, dlaczego nie jest to wątkowo bezpieczne, nie dzieje się tak dlatego, że pierwsze wywołanie
instance()
musi wywołać konstruktoraSingleton s
. Aby zapewnić bezpieczeństwo wątków, musiałoby to nastąpić w sekcji krytycznej, ale standard nie wymaga, aby została wybrana sekcja krytyczna (dotychczasowy standard całkowicie milczy na temat wątków). Kompilatory często implementują to za pomocą prostego sprawdzenia i inkrementacji statycznej wartości logicznej - ale nie w krytycznej sekcji. Coś w rodzaju następującego pseudokodu:Oto prosty, bezpieczny wątkowo Singleton (dla Windows). Używa prostego opakowania klasy dla obiektu CRITICAL_SECTION systemu Windows, dzięki czemu kompilator może automatycznie zainicjować
CRITICAL_SECTION
poprzedniąmain()
wywołanie. Idealnie byłoby użyć prawdziwej klasy sekcji krytycznej RAII, która może obsługiwać wyjątki, które mogą wystąpić, gdy sekcja krytyczna jest utrzymywana, ale to wykracza poza zakres tej odpowiedzi.Podstawową operacją jest to, że gdy
Singleton
żądane jest wystąpienie of , zostaje przyjęta blokada, tworzony jest singleton, jeśli to konieczne, a następnie blokada jest zwalniana i zwracane jest odniesienie do singletona.Człowieku - to dużo bzdur, żeby „uczynić świat lepszym”.
Główne wady tej implementacji (jeśli nie przepuściłem kilku błędów) to:
new Singleton()
rzucony, blokada nie zostanie zwolniona. Można to naprawić, używając prawdziwego obiektu blokady RAII zamiast prostego, który mam tutaj. Może to również pomóc w uczynieniu rzeczy przenośnymi, jeśli używasz czegoś takiego jak Boost, aby zapewnić niezależne od platformy opakowanie blokady.main()
wywołaniu - jeśli wywołasz ją wcześniej (jak w przypadku inicjalizacji obiektu statycznego), rzeczy mogą nie działać, ponieważCRITICAL_SECTION
może nie zostać zainicjowany.źródło
new Singleton()
rzuci?new Singleton()
rzuca, zdecydowanie występuje problem z blokadą. Należy użyć odpowiedniej klasy blokady RAII, coś w rodzajulock_guard
Boost. Chciałem, żeby przykład był mniej lub bardziej samodzielny, a był już trochę potworem, więc odrzuciłem bezpieczeństwo wyjątków (ale je odwołałem). Może powinienem to naprawić, aby ten kod nie został wycięty i wklejony w niewłaściwym miejscu.Patrząc na następny standard (sekcja 6.7.4), wyjaśnia, jak statyczna inicjalizacja lokalna jest bezpieczna dla wątków. Kiedy więc ta sekcja standardu zostanie szeroko zaimplementowana, Meyer's Singleton będzie preferowaną implementacją.
Nie zgadzam się już z wieloma odpowiedziami. Większość kompilatorów już implementuje statyczną inicjalizację w ten sposób. Jedynym godnym uwagi wyjątkiem jest Microsoft Visual Studio.
źródło
Prawidłowa odpowiedź zależy od Twojego kompilatora. Może zdecydować, aby był bezpieczny dla wątków; nie jest „naturalnie” bezpieczne dla wątków.
źródło
Na większości platform nie jest to bezpieczne dla wątków. (Dołącz zwykłe zastrzeżenie wyjaśniające, że standard C ++ nie wie o wątkach, więc zgodnie z prawem nie mówi, czy tak jest, czy nie).
Przyczyną tego nie jest to, że nic nie stoi na przeszkodzie, aby więcej niż jeden wątek wykonywał jednocześnie
s
konstruktora.„C ++ and the Perils of Double-Checked Locking” Scotta Meyersa i Andrei Alexandrescu to całkiem niezły traktat na temat singletonów bezpiecznych dla wątków.
źródło
Jak powiedział MSalters: To zależy od używanej implementacji C ++. Sprawdź dokumentację. A jeśli chodzi o drugie pytanie: „Jeśli nie, to dlaczego?” - Standard C ++ nie wspomina jeszcze nic o wątkach. Jednak nadchodząca wersja C ++ jest świadoma wątków i wyraźnie stwierdza, że inicjalizacja statycznych lokalizacji lokalnych jest bezpieczna dla wątków. Jeśli dwa wątki wywołują taką funkcję, jeden wątek wykona inicjalizację, podczas gdy drugi będzie blokował i czekał na zakończenie.
źródło