Czy ktoś może wyjaśnić różnicę między:
- lock (someobject) {}
- Korzystanie z Mutex
- Korzystanie z semafora
- Korzystanie z monitora
- Korzystanie z innych klas synchronizacji .Net
Po prostu nie mogę tego rozgryźć. Wydaje mi się, że pierwsze dwa są takie same?
c#
multithreading
synchronization
locking
mutex
user38834
źródło
źródło
Odpowiedzi:
Świetne pytanie. Może się mylę… Spróbuję… Wersja nr 2 mojej pierwotnej odpowiedzi… z odrobiną większego zrozumienia. Dzięki za zmuszenie mnie do czytania :)
zamek (obj)
Monitory
Korzystanie z programu Monitor jest generalnie preferowane zamiast muteksów, ponieważ monitory zostały zaprojektowane specjalnie dla platformy .NET Framework i dzięki temu lepiej wykorzystują zasoby.
Korzystanie z blokady lub monitora jest przydatne do zapobiegania równoczesnemu wykonywaniu bloków kodu wrażliwych na wątki, ale te konstrukcje nie pozwalają jednemu wątkowi na komunikowanie zdarzenia z innym. Wymaga to zdarzeń synchronizacji , które są obiektami, które mają jeden z dwóch stanów, sygnalizowanym i nie sygnalizowanym, których można używać do aktywowania i zawieszania wątków. Mutex, semafory to koncepcje na poziomie systemu operacyjnego. np. z nazwanym muteksem można synchronizować wiele (zarządzanych) exe (upewniając się, że tylko jedno wystąpienie aplikacji działa na komputerze).
Muteks:
Semafory (ranią mój mózg).
STRONA DO CZYTANIA - Synchronizacja wątków (C #)
źródło
Monitor
nie pozwala na komunikację, jest niepoprawne; nadal możeszPulse
itd. zMonitor
Re „Korzystanie z innych klas synchronizacji .Net” - kilka innych, o których warto wiedzieć:
Istnieje również więcej konstrukcji blokujących (o niskim narzutie) w CCR / TPL ( Parallel Extensions CTP) - ale IIRC, zostaną one udostępnione w .NET 4.0
źródło
Jak stwierdzono w ECMA i jak można zauważyć z metod Reflected, instrukcja lock jest w zasadzie równoważna z
Z powyższego przykładu widzimy, że Monitory mogą blokować obiekty.
Mutexe są przydatne, gdy potrzebujesz synchronizacji międzyprocesowej, ponieważ mogą blokować identyfikator ciągu. Ten sam identyfikator ciągu może być używany przez różne procesy w celu uzyskania blokady.
Semafory są jak muteksy na sterydach, umożliwiają równoczesny dostęp, zapewniając maksymalną liczbę jednoczesnych dostępów ”. Po osiągnięciu limitu semafor zaczyna blokować dalszy dostęp do zasobu, dopóki jeden z wywołujących nie zwolni semafora.
źródło
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
źródło
Dodatkowym zastrzeżeniem dotyczącym blokowania dowolnego współużytkowanego Mutexu zidentyfikowanego za pomocą identyfikatora ciągu jest to, że domyślnie będzie to mutex „Lokalny \” i nie będzie współdzielony między sesjami w środowisku serwera terminali.
Przedrostek identyfikatora w postaci ciągu znaków „Global \” zapewnia odpowiednią kontrolę dostępu do współdzielonych zasobów systemowych. Właśnie napotkałem całą masę problemów z synchronizacją komunikacji z usługą działającą w ramach konta SYSTEM, zanim zdałem sobie z tego sprawę.
źródło
Starałbym się unikać "lock ()", "Mutex" i "Monitor", jeśli możesz ...
Sprawdź nową przestrzeń nazw System.Collections.Concurrent w .NET 4
Ma kilka ładnych klas kolekcji bezpiecznych wątkowo
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
ConcurrentDictionary rządzi! nie ma już dla mnie ręcznego blokowania!
źródło
W większości przypadków nie należy używać blokad (= Monitory) ani muteksów / semaforów. Wszystkie blokują bieżący wątek.
I zdecydowanie nie powinieneś używać
System.Collections.Concurrent
klas - są głównym źródłem warunków wyścigu, ponieważ nie obsługują transakcji między wieloma kolekcjami, a także blokują bieżący wątek.Zaskakujące jest, że .NET nie ma skutecznych mechanizmów synchronizacji.
Zaimplementowałem kolejkę szeregową z GCD (
Objc/Swift
world) na C # - bardzo lekkie, nieblokujące narzędzie do synchronizacji wykorzystujące pulę wątków, z testami.W większości przypadków jest to najlepszy sposób na zsynchronizowanie wszystkiego - od dostępu do bazy danych (hello sqlite) po logikę biznesową.
źródło