Jak wybrać między Semaphore a SemaphoreSlim?

109

Ich publiczne interfejsy wyglądają podobnie. W dokumentacji stwierdza, że SemaphoreSlim to lekka alternatywa i nie używa semaforów jądra systemu Windows. Ten zasób stwierdza, że ​​SemaphoreSlim jest znacznie szybszy. W jakich sytuacjach SemaphoreSlim ma większy sens niż Semaphore i odwrotnie?

Michael Hedgpeth
źródło
1
Semafor zawsze przekazuje zadanie do systemu operacyjnego. To sprawia, że ​​jest stosunkowo drogie, jeśli semafor nie jest silnie kwestionowany, wywołanie jądra z łatwością kosztuje 400 nanosekund. Szczupła przysługa najpierw próbuje zrobić to tanio ze wspólną zmienną, a wywołuje system operacyjny tylko wtedy, gdy to nie zadziałało. Zawsze lubisz tanio, z wyjątkiem przypadku narożnego, w którym semafor musi być współdzielony przez wiele procesów.
Hans Passant

Odpowiedzi:

64

Jedna różnica polega na tym, SemaphoreSlimże nie zezwala na nazwane semafory, które mogą być stosowane w całym systemie. Oznaczałoby to, że SemaphoreSlim nie może być używany do synchronizacji między procesami.

Dokumentacja MSDN wskazuje również, że SemSlim powinno być używane, gdy „oczekuje się, że czas oczekiwania będzie bardzo krótki”. Zwykle dobrze pasowałoby to do pomysłu, że smukła wersja jest lżejsza w większości kompromisów.

Andrew Barber
źródło
66
Chciałbym, żeby firma MS napisała coś o tym, co się dzieje, gdy czasy oczekiwania nie są „bardzo krótkie”.
John Reynolds,
79
... lub co oznacza „bardzo krótkie”
Richard Szalay,
9
W zależności od systemu, 1 mikrosekunda dla Semaphore i 1/4 mikrosekundy dla SemaphoreSlim na wykonanie WaitOne lub Release ["C # 5.0 w pigułce" str. 890] Więc może to mają na myśli bardzo krótkie czasy oczekiwania?
David Sherret
2
@dhsto Dobre odniesienie! Ale zgaduję, że powiązany artykuł oznacza „kiedy czas oczekiwania na dostęp do udostępnionego zasobu jest bardzo krótki” - tj. Zasób nie pozostanie w wyłącznym użytku przez długi czas - zamiast oznaczać czas oczekiwania na sam kod semafora? Kod semafora będzie zawsze wykonywany, niezależnie od tego, jak długo zasób jest zablokowany.
culix
4
@culix Patrząc na źródło dla Semaphore versus SemaphoreSlim Podejrzewam, że głównym powodem, dla którego SemaphoreSlim powinien być używany tylko do „bardzo krótkich” czekań, jest to, że używa on czekania spinowego. Jest to bardziej responsywne, ale pochłania dużo procesora i byłoby marnotrawstwem w przypadku dłuższych okresów oczekiwania, w których natychmiastowe odpowiedzi są mniej ważne. Semafor zamiast tego blokuje wątek.
r2_118
17

Dokumentacja MSDN opisuje różnicę.

Jednym zdaniem:

  • Klasa SemaphoreSlim reprezentuje lekki, szybki semafor, którego można używać do oczekiwania w ramach pojedynczego procesu, gdy oczekuje się, że czasy oczekiwania będą bardzo krótkie.
Joe
źródło
1
Aby to dodać, użyj SemaphoreSlim we wszystkich przypadkach, w których aplikacja jest jedynym procesem na komputerze, który będzie musiał uzyskać dostęp do tego semafora.
Austin Salgat
2
@Salgat Dlaczego miałbyś to zrobić? Jak opisano w innych odpowiedziach, SemaphoreSlimjest zaimplementowany za pomocą SpinWait, więc jeśli będziesz na to dużo czekać, stracisz dużo czasu procesora. Co nie jest nawet dobrym pomysłem, jeśli twój proces jest jedynym procesem na komputerze.
M.Stramm
W dokumentacji MSDN: „Klasa SemaphoreSlim jest zalecanym semaforem do synchronizacji w ramach jednej aplikacji”. Z wyjątkiem specjalnych przypadków, należy domyślnie używać SemaphoreSlim, jeśli tylko jeden proces używa tego semafora. Istnieją specjalne wyjątki dla każdego współbieżnego typu danych, co jest oczywiste.
Austin Salgat
17

SemaphoreSlim jest oparty na SpinWait i Monitor, więc wątek, który czeka na uzyskanie blokady, wypala cykle procesora przez pewien czas w nadziei, że uzyska blokadę przed przejściem do innego wątku. Jeśli tak się nie stanie, wątki umożliwiają systemom zmianę kontekstu i ponowną próbę (przez spalenie kilku cykli procesora), gdy system operacyjny ponownie zaplanuje ten wątek. Przy długim oczekiwaniu ten wzorzec może przepalić znaczną liczbę cykli procesora. Tak więc najlepszym scenariuszem takiej implementacji jest sytuacja, gdy przez większość czasu nie ma czasu oczekiwania i możesz niemal natychmiast uzyskać blokadę.

Semafor opiera się na implementacji w jądrze systemu operacyjnego, więc za każdym razem, gdy nabywasz blokadę, spędzasz sporo cykli procesora, ale po tym wątek po prostu śpi tak długo, jak to konieczne, aby uzyskać blokadę.

Dmytro Zakharov
źródło
12

Odnośnie kontrowersji dotyczących „krótkich czasów”:

Przynajmniej dokumentacja SemaphoreSlim MSDN stwierdza, że

Klasa SemaphoreSlim jest zalecanym semaforem do synchronizacji w ramach jednej aplikacji.

w sekcji Uwagi. Ta sama sekcja mówi również o głównej różnicy między Semaphore i SemaphoreSlim:

SemaphoreSlim jest lekką alternatywą dla klasy Semaphore, która nie używa semaforów jądra systemu Windows. W przeciwieństwie do klasy Semaphore, klasa SemaphoreSlim nie obsługuje nazwanych semaforów systemowych. Możesz go używać tylko jako lokalnego semafora.

Shaedar
źródło
1
Więcej szczegółów: [SemaphoreSlim and other locks] use busy spinning for brief periods before they put the thread into a true Wait state. When wait times are expected to be very short, spinning is far less computationally expensive than waiting, which involves an expensive kernel transition: dotnet.github.io/docs/essentials/collections/...
Marco Sulla
0

Spojrzałem tutaj na kod źródłowy i oto co wymyśliłem:

  1. Zarówno Semaphore, jak i SemaphoreSlim pochodzą z WaitHandle, który wewnętrznie używa natywnego dojścia Win32. Dlatego musisz Dispose () oba. Tak więc pogląd, że Slim jest lekki, jest podejrzany.

  2. SemaphoreSlim używa SpinWait wewnętrznie, podczas gdy Semaphore tego nie robi. To mówi mi, że w przypadkach, w których oczekuje się, że oczekiwanie będzie długie, Semaphore powinien działać lepiej przynajmniej w tym sensie, że nie zablokuje twojego procesora.

ILIA BROUDNO
źródło
2
SemaphoreSlim nie pochodzi od WaitHandle. Właściwie został stworzony z jednym celem - aby wyeliminować pochodzenie z WaitHandle, który jest prymitywem przestrzeni jądra, w zadaniach, w których musisz zsynchronizować operacje w ramach tylko jednego procesu.
Igor V Savchenko