2010-12-30 17 views
5

Załóżmy, że mam funkcja, która stara się chronić globalny licznik użyciu tego kodu:Czy budowa obiektu funkcji statycznej zakresu jest bezpieczna dla wątków?

static MyCriticalSectionWrapper lock; 
lock.Enter(); 
counter = ++m_counter; 
lock.Leave(); 

Czy istnieje szansa, że ​​dwa wątki wywoła konstruktor lock „s? Jaki jest bezpieczny sposób na osiągnięcie tego celu?

+0

Zobacz również http://stackoverflow.com/questions/55510/when-do-function-level-static-variables-get-allocated-initialized –

Odpowiedz

4

Utworzenie samego obiektu blokady nie jest bezpieczne dla wątków. W zależności od kompilatora można utworzyć wiele niezależnych obiektów blokady, jeśli wiele wątków wchodzi do funkcji w (prawie) tym samym czasie.

Rozwiązaniem tego problemu jest zastosowanie:

  • OS gwarantowane jeden intialization czas (dla obiektu blokady)
  • Double-checked locking (zakładając, że jest bezpieczny dla konkretnego przypadku)
  • Wątek bezpieczne singleton dla obiektu blokady
  • Dla konkretnego przykładu, możesz użyć funkcji blokowania wątków (np. funkcja InterlockedIncrement() dla Windows) dla przyrostu i uniknięcia blokowania łącznie
+0

Następnie, aby uczynić go bezpiecznym, muszę zapakować go w krytyczną sekcję, prowadząc do tego samego problemu ... prawda? –

+1

Singleton bezpieczny dla wątków jest trudniejszy do napisania, niż mogłoby się wydawać. Lepszym rozwiązaniem jest wyeliminowanie globalnego i użycie aktywnego obiektu. Aktywny obiekt będzie wówczas odpowiedzialny za wszelkie działania, które zamierzasz zrobić z globalnym. – wilhelmtell

+5

AFAIK, blokada z podwójnym sprawdzeniem nie jest uważana za wątkową. To rodzaj anty-wzoru. –

-1

To zależy od implementacji zamka.

+0

Proszę wyjaśnić. Możesz założyć proste opakowanie CRITICAL_SECTION za pomocą 'Init()' w konstruktorze, a 'Enter()' i 'Leave()' po prostu przekazać do Windows API. Z warunkową instrukcją kompilacji do rozgałęzienia do innych implementacji systemu operacyjnego, ale nie o to chodzi. –

+0

-1 Nie sądzę, że twoje oświadczenie jest poprawne. Inicjalizacja (budowa) 'blokady' jest wykonywana, gdy funkcja jest najpierw wywoływana, ale jeśli dwa wątki wywołują funkcję w tym samym czasie przed zainicjowaniem zmiennej' lock', wtedy każdy wątek może pomyślnie utworzyć własną blokadę. Następnie, niezależnie od poprawności implementacji zamka, oba wątki mogą wykonywać kod krytyczny w tym samym czasie. –

+0

@DanielTrebbien Nie. Jeśli zamek jest poprawnie napisany, dwa wątki ** nie ** będą w stanie go zdobyć w tym samym czasie. Na tym właśnie polega cały zamek, a jeśli go nie spełnia, to nie jest zamek, ale błąd. Właściwie napisana blokada C++ nabędzie podczas budowy i wydania w momencie zniszczenia. Wtedy nie ma potrzeby wykonywania kolejnych wywołań 'lock.Enter()' i 'lock.Leave()'. To jest RAII. Jeśli nie uda się uzyskać ctor lock, to powinien wyrzucić. Ctor musi mieć sekcję atomową, aby to zadziałało. – wilhelmtell

1

Wywołanie konstruktora może być zależne od implementacji i/lub środowiska wykonawczego, ale nie jest to scoped_lock, więc nie stanowi problemu.

Główna operacja jest odpowiednio chroniona przed dostępem do wielu wątków Myślę, że.

(Wiesz, globalny globalny, funkcja statycznego dla funkcji statycznego Zmienna blokady musi być zdefiniowana w tym samym zakresie ze strzeżonego obiektu.).

+0

"zmienna blokady musi być zdefiniowana w tym samym zasięgu, co chroniony obiekt." - to dobra zasada. Dziękuję Ci. –

0

oryginalny przykładowy kod:

static MyCriticalSectionWrapper lock; 
lock.Enter(); 
counter = ++m_counter; 
lock.Leave(); 

I uświadomienie sobie, że kod licznika jest prawdopodobnie tylko symbolem zastępczym, ale jeśli faktycznie jest to, co próbujesz zrobić, możesz użyć funkcji Windows "InterlockedIncrement()", aby to osiągnąć. Przykład:

// atomic increment for thread safety 
InterlockedIncrement(&m_counter); 
counter = m_counter; 
+0

lub std :: atomic w tych dniach – paulm

Powiązane problemy