Najgorsze (tak naprawdę nie działa)
Zmień modyfikator dostępu counter
napublic volatile
Jak wspomnieli inni, samo to nie jest wcale bezpieczne. Chodzi o volatile
to, że wiele wątków działających na wielu procesorach może i będzie buforować dane i ponownie zamawiać instrukcje.
Jeśli tak nie jest volatile
, a CPU A zwiększa wartość, wówczas CPU B może nie zobaczyć tej zwiększonej wartości do pewnego czasu, co może powodować problemy.
Jeśli tak volatile
, to tylko zapewnia, że dwa procesory zobaczą te same dane w tym samym czasie. Nie powstrzymuje ich to wcale od przeplatania swoich odczytów i operacji zapisu, co jest problemem, którego próbujesz uniknąć.
Drugi najlepszy:
lock(this.locker) this.counter++
;
Jest to bezpieczne (pod warunkiem, że pamiętasz lock
wszędzie, gdzie masz dostęp this.counter
). Zapobiega wykonywaniu przez każdy inny wątek kodu, który jest chroniony locker
. Korzystanie z blokad również zapobiega problemom związanym z ponownym zamawianiem wielu procesorów, co jest świetne.
Problem polega na tym, że blokowanie jest powolne, a jeśli użyjesz ponownie locker
w innym miejscu, które nie jest tak naprawdę powiązane, możesz zablokować inne wątki bez powodu.
Najlepsza
Interlocked.Increment(ref this.counter);
Jest to bezpieczne, ponieważ skutecznie odczytuje, zwiększa i zapisuje „jedno trafienie”, którego nie można przerwać. Z tego powodu nie wpłynie to na żaden inny kod i nie musisz pamiętać o blokowaniu w innym miejscu. Jest również bardzo szybki (jak mówi MSDN, w nowoczesnych procesorach jest to często dosłownie jedna instrukcja procesora).
Nie jestem jednak do końca pewien, czy obejdzie inne procesory zmieniające porządek, czy też trzeba połączyć zmienność z przyrostem.
Zablokowane Uwagi:
- METODY ZABLOKOWANE SĄ WSPÓŁCZESNIE BEZPIECZNE NA KAŻDEJ liczbie rdzeni lub procesorów.
- Zablokowane metody stosują pełne ogrodzenie wokół wykonywanych instrukcji, więc zmiana kolejności nie następuje.
- Metody blokowane nie potrzebują, a nawet nie wspierają dostępu do pola niestabilnego , ponieważ zmienny jest umieszczony w połowie pola wokół operacji na danym polu, a blokowany wykorzystuje pełne ogrodzenie.
Przypis: Do tego, co lotne jest w rzeczywistości dobre.
Ponieważ volatile
nie zapobiega tego rodzaju problemom z wielowątkowością, po co to jest? Dobrym przykładem jest powiedzenie, że masz dwa wątki, jeden, który zawsze zapisuje zmienną (powiedzmy queueLength
), i ten, który zawsze czyta z tej samej zmiennej.
Jeśli queueLength
nie jest niestabilny, wątek A może pisać pięć razy, ale wątek B może postrzegać te zapisy jako opóźnione (lub nawet potencjalnie w niewłaściwej kolejności).
Rozwiązaniem byłoby zablokowanie, ale w tej sytuacji można również użyć niestabilności. Zapewni to, że wątek B zawsze będzie widział najbardziej aktualną rzecz, jaką napisał wątek A. Zauważ jednak, że ta logika działa tylko wtedy, gdy masz pisarzy, którzy nigdy nie czytają, i czytelników, którzy nigdy nie piszą, i jeśli to, co piszesz, jest wartością atomową. Jak tylko wykonasz pojedynczy odczyt-modyfikacja-zapis, musisz przejść do operacji zablokowanych lub użyć blokady.