Zrobiłem zajęcia i obsługę CLR dla wątków w DotGNU i mam kilka przemyśleń ...
O ile nie potrzebujesz blokad krzyżowych, zawsze powinieneś unikać używania Mutex & Semaphores. Te klasy w .NET są opakowaniami wokół Win32 Mutex i Semaphores i mają dość dużą wagę (wymagają przełączania kontekstu na jądro, co jest drogie - szczególnie jeśli blokada nie jest kwestionowana).
Jak wspomnieliśmy inni, instrukcja blokady C # jest magią kompilatora dla Monitor.Enter i Monitor.Exit (istniejąca w try / w końcu).
Monitory mają prosty, ale potężny mechanizm sygnału / oczekiwania, którego Muteksy nie mają za pośrednictwem metod Monitor.Pulse / Monitor.Wait. Odpowiednikiem Win32 byłyby obiekty zdarzeń za pośrednictwem CreateEvent, które faktycznie istnieją również w .NET jako WaitHandles. Model Pulse / Wait jest podobny do uniksowych pthread_signal i pthread_wait, ale są szybsze, ponieważ mogą być działaniami całkowicie w trybie użytkownika w przypadku niekontrolowanym.
Monitor.Pulse / Wait jest prosty w użyciu. W jednym wątku blokujemy obiekt, sprawdzamy flagę / stan / właściwość i jeśli nie tego oczekujemy, wywołujemy Monitor.Wait, który zwalnia blokadę i czeka na wysłanie impulsu. Kiedy czas oczekiwania powraca, wykonujemy pętlę i ponownie sprawdzamy flagę / stan / właściwość. W drugim wątku blokujemy obiekt za każdym razem, gdy zmieniamy flagę / stan / właściwość, a następnie wywołujemy PulseAll, aby obudzić wątki nasłuchujące.
Często chcemy, aby nasze klasy były bezpieczne dla wątków, więc umieszczamy blokady w naszym kodzie. Jednak często jest tak, że nasza klasa będzie używana tylko przez jeden wątek. Oznacza to, że blokady niepotrzebnie spowalniają nasz kod ... w tym miejscu sprytne optymalizacje w środowisku CLR mogą pomóc poprawić wydajność.
Nie jestem pewien co do implementacji blokad firmy Microsoft, ale w DotGNU i Mono flaga stanu blokady jest przechowywana w nagłówku każdego obiektu. Każdy obiekt w .NET (i Javie) może stać się blokadą, więc każdy obiekt musi to obsługiwać w swoim nagłówku. W implementacji DotGNU istnieje flaga, która pozwala na użycie globalnej tablicy hashy dla każdego obiektu używanego jako blokada - ma to tę zaletę, że eliminuje 4-bajtowe obciążenie dla każdego obiektu. Nie jest to dobre rozwiązanie dla pamięci (szczególnie dla systemów wbudowanych, które nie są mocno wątkowe), ale ma wpływ na wydajność.
Zarówno Mono, jak i DotGNU skutecznie używają muteksów do wykonywania blokowania / oczekiwania, ale używają operacji porównania i wymiany w stylu spinlock, aby wyeliminować potrzebę rzeczywistego wykonywania twardych blokad, chyba że jest to naprawdę konieczne:
Przykład implementacji monitorów można zobaczyć tutaj:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup