Dwukrotnie sprawdzane blokowanie-Wikipédia

before-content-x4

W inżynierii oprogramowania, Podwójne blokowanie testu Lub Dwukrotnie sprawdzone blokowanie jest byłym szefem projektowym [[[ Pierwszy ] .

after-content-x4

Uważany dzisiaj za antypattern ze względu na subtelne problemy i trudne do wykrycia, w przeszłości używano go do zmniejszenia dodatkowego kosztu nabycia zamka niezbędnego do późnej inicjalizacji singletonu, zaczynając od sprawdzenia obiektu zamka Bez ostrożności wcześniej, aby faktycznie umieścić zamek.

Weźmy na przykład następującą klasę Java:

// wersja mono-{{język | in | text = wątek}}  klasa  Singleton1  {  prywatny  Narzędzie  narzędzie  =  zero ;  publiczny  Narzędzie  Getoutil ()  {  Jeśli  ( narzędzie  ==  zero )  {  narzędzie  =  nowy  Narzędzie ();  }  powrót  narzędzie ;  }  // Inne atrybuty i metody ...  }  

Problem tej klasy polega na tym, że nie działa w multi- nitka . Jeśli dwa wątki Zadzwoń po raz pierwszy Getoutil () Jednocześnie wszyscy utworzą obiekt w tym samym czasie, co drugi lub jeden otrzyma odniesienie do częściowo zainicjowanego obiektu. Aby tego uniknąć, musisz użyć blokady, która odbywa się dzięki synchronizacji, jak pokazano na przykładzie kodu poniżej.

// poprawna wersja w środowisku wielu-{{język | in | text = wątek}}, ale potencjalnie kosztowne  klasa  Singleton2  {  prywatny  Narzędzie  narzędzie  =  zero ;  publiczny  zsynchronizowane  Narzędzie  Getoutil ()  {  Jeśli  ( narzędzie  ==  zero )  {  narzędzie  =  nowy  Narzędzie ();  }  powrót  narzędzie ;  }  // Inne atrybuty i metody ...  }  

Tutaj, pierwsze wezwanie Getoutil () utworzy obiekt i jeśli inne wątki dzwonić Getoutil () Podczas gdy obiektem jest lekcje inicjalizacji, zostaną one wstrzymane przez synchronizację, dopóki obiekt nie zostanie całkowicie zainicjowany nitka . Wszystkie następujące połączenia zwrócą tylko odniesienie do wcześniej zainicjowanego obiektu.

Jednak synchronizacja metody może znacznie zwiększyć czas jej wykonania [[[ 2 ] . Pozyskiwanie i uwalnianie zamka za każdym razem, gdy nazywana jest metoda może wydawać się niepotrzebnym dodatkowym kosztem. Wielu programistów próbowało zoptymalizować ten kod w następujący sposób:

  1. Sprawdź, czy zmienna nie została jeszcze zainicjowana (bez próby uzyskania zamka). Jeśli tak, natychmiast obróć swoją wartość;
  2. zdobyć zamek;
  3. Ponownie przekonuj, że zmienna nie została jeszcze zainicjowana: jeśli inna nitka Nabył blokadę tuż wcześniej, w międzyczasie mógł zainicjować zmienną. Jeśli okaże się, że zmienna jest inicjowana, zwróć jej wartość;
  4. W przeciwnym razie zainicjuj zmienną i zwróć jej wartość.
// Wersja {{język | in | text = multitreaded}} błędny  // motyw „Dwukrotnie sprawdzane blokowanie”  klasa  Singleton3  {  prywatny  Narzędzie  narzędzie  =  zero ;  publiczny  Narzędzie  Getoutil ()  {  Jeśli  ( narzędzie  ==  zero )  {  zsynchronizowane  ( Ten )  {  Jeśli  ( narzędzie  ==  zero )  {  narzędzie  =  nowy  Narzędzie ();  }  }  }  powrót  narzędzie ;  }  // Inne atrybuty i metody ...  }  

Intuicyjnie algorytm ten wydaje się skutecznym rozwiązaniem postawionego problemu. Jednak podnosi subtelne i trudne do śledzenia problemy. W ten sposób rozważ sekwencję następujących zdarzeń:

after-content-x4
  1. . nitka A Należy pamiętać, że zmienna współdzielona nie jest inicjowana. Dlatego nabywa zamek i zaczyna inicjować zmienną;
  2. Ze względu na semantykę języka programowania kod generowany przez kompilatora ma prawo do modyfikowania wcześniejszej zmiennej A zakończył inicjalizację, tak że zmienna odwołuje się do częściowo zainicjowanego obiektu (na przykład dlatego nitka Zacznij od przydzielania pamięci, a następnie umieszcza odniesienie do bloku pamięci przydzielonego w zmiennej, przed, wreszcie, do wywołania producenta, który zainicjuje blok pamięci);
  3. . nitka B Zauważ, że wspólna zmienna została zainicjowana (a przynajmniej wydaje się być) i zwraca swoją wartość natychmiast bez próby uzyskania zamka. Jeśli, wówczas współdzielona zmienna jest używana wcześniej A nie miał czasu na zakończenie inicjalizacji, program prawdopodobnie sadzi.

Jeden z niebezpieczeństw Dwukrotnie sprawdzone blokowanie Czy to często wydaje się działać. To zależy od kompilatora, jak wątki są zamawiane przez system operacyjny, a także inne mechanizmy zarządzania dostępem do danych do danych. Odtwarzanie przypadków sadzenia może być trudniejsze, ponieważ są wysoce nieprawdopodobne, gdy kod jest wykonywany w debuggerze. Sposób użycia Dwukrotnie sprawdzone blokowanie Dlatego musi zostać zakazany jak najwięcej.

Niemniej jednak Javase 5.0 oferuje rozwiązanie tego problemu ze słowem zarezerwowanym niestabilnym, co to gwarantuje wątki Różnie właściwie zarządzać konkurencyjnym dostępem do pojedynczej instancji singletonu [[[ 3 ] :

// działa dzięki nowej semantyce niestabilnego słowa kluczowego  // nie działa z Java 1.4 lub poprzednim  klasa  Singleton4  {  prywatny  lotny  Narzędzie  narzędzie  =  zero ;  publiczny  Narzędzie  Getoutil ()  {  Jeśli  ( narzędzie  ==  zero )  {  zsynchronizowane  ( Ten )  {  Jeśli  ( narzędzie  ==  zero )  {  narzędzie  =  nowy  Narzędzie ();  }  }  }  powrót  narzędzie ;  }  // Inne atrybuty i metody ...  }  

Jednak bezpieczeństwo dostępu ma cenę: dostęp do zmiennej zmiennej jest mniej skuteczny niż dostęp do zmiennej normalnej.

Wiele wersji szefa Dwukrotnie sprawdzone blokowanie które nie stosują jawnej synchronizacji lub zmiennej zmiennej, zostały zaproponowane. Oprócz tych opartych na słowie kluczowym enum lub motyw wsparcia inicjalizacji na żądanie, wszystkie okazały się niepoprawne [[[ 4 ] W [[[ 5 ] .

Możemy wdrożyć Dwukrotnie sprawdzone blokowanie Z wizualnym C ++ 2005, jeśli wskaźnik w kierunku zasobu zostanie zadeklarowany lotny . Wizualne C ++ 2005 gwarantuje, że zmienne zmienne zachowują się jak bariery, podobnie jak w przypadku Javase 5.0, uniemożliwiając zarówno kompilator, jak i procesor reordowania odczytów i wpisów do tych zmiennych, aby były bardziej skuteczne.

Poprzednie wersje Visual C ++ nie oferowały tej gwarancji. Zaznacz wskaźnik w kierunku zasobu, ponieważ niestabilne kara wydajność w taki czy inny sposób, szczególnie jeśli wskaźnik jest widoczny w innym miejscu kodu: kompilator traktuje go jako barierę wszędzie tam, gdzie jest używany, nawet jeśli nie jest to konieczne.

  1. Schmidt, zm i in. W Architektura oprogramowania zorientowana na wzory W tom. 2, 2000, P. 353-363
  2. Boehm, Hans-J., Wątków nie można wdrożyć jako biblioteki , ACM 2005, P. 265
  3. Nowa semantyka słowa kluczowego lotny jest opisany w [Pierwszy] .
  4. [2]
  5. [3]

Powiązane artykuły [[[ modyfikator |. Modyfikator i kod ]

Linki zewnętrzne [[[ modyfikator |. Modyfikator i kod ]

after-content-x4