Udostępnij za pośrednictwem


Omówienie elementów pierwotnych synchronizacji

Platforma .NET udostępnia szereg typów, których można użyć do synchronizowania dostępu do współdzielonego zasobu lub koordynowania interakcji wątków.

Ważne

Użyj tego samego wystąpienia pierwotnego synchronizacji, aby chronić dostęp do udostępnionego zasobu. ** Jeśli używasz różnych wystąpień prymitywów synchronizacji do ochrony tego samego zasobu, możesz obejść ochronę zapewnianą przez prymitywy synchronizacji.

Klasa WaitHandle i lekkie typy synchronizacji

Wiele prymitywów synchronizacji platformy .NET dziedziczy z klasy System.Threading.WaitHandle, która hermetyzuje natywny uchwyt synchronizacyjny systemu operacyjnego i wykorzystuje mechanizm sygnalizacji do interakcji między wątkami. Klasy te obejmują:

  • System.Threading.Mutex, który udziela wyłącznego dostępu do zasobu udostępnionego. Stan mutexu jest sygnalizowany, jeśli żaden wątek nie jest jego właścicielem.
  • System.Threading.Semaphore, który ogranicza liczbę wątków, które mogą uzyskiwać dostęp do współużytkowanego zasobu lub puli zasobów jednocześnie. Stan semafora jest ustawiony na sygnał, gdy jego liczba jest większa niż zero, a niepodpisane, gdy jej liczba wynosi zero.
  • System.Threading.EventWaitHandle, który reprezentuje zdarzenie synchronizacji wątków i może znajdować się w stanie sygnalizowanym lub niesygnalizowanym.
  • System.Threading.AutoResetEvent, który pochodzi z EventWaitHandle i po zasygnalizowaniu automatycznie resetuje się do stanu niezasygnalizowanego po zwolnieniu pojedynczego oczekującego wątku.
  • System.Threading.ManualResetEvent, który pochodzi z EventWaitHandle i, gdy jest zasygnalizowany, pozostaje w stanie zasygnalizowania, dopóki nie zostanie wywołana metoda Reset.

W programie .NET Framework, ponieważ WaitHandle dziedziczy z System.MarshalByRefObject, te typy mogą służyć do synchronizowania działań wątków poza granicami domen aplikacji.

W programach .NET Framework, .NET Core i .NET 5 lub nowszych niektóre z tych typów mogą reprezentować nazwane dojścia synchronizacji systemu, które są widoczne w całym systemie operacyjnym i mogą być używane do synchronizacji między procesami:

Aby uzyskać więcej informacji, zobacz dokumentację interfejsu WaitHandle API.

Lekkie typy synchronizacji nie opierają się na podstawowych uchwytach systemu operacyjnego i zazwyczaj zapewniają lepszą wydajność. Nie można ich jednak używać do synchronizacji między procesami. Użyj tych typów do synchronizacji wątków w jednej aplikacji.

Niektóre z tych typów to alternatywy dla typów wywodzących się z WaitHandle. Na przykład SemaphoreSlim jest lekką alternatywą dla Semaphore.

Synchronizacja dostępu do zasobu udostępnionego

Platforma .NET udostępnia szereg prymitywów synchronizacji w celu kontrolowania dostępu do współdzielonego zasobu przez wiele wątków.

Klasa monitorów

Klasa System.Threading.Monitor zapewnia wzajemnie wykluczający się dostęp do zasobu współdzielonego poprzez założenie lub zwolnienie blokady na obiekcie, który identyfikuje ten zasób. Gdy blokada jest utrzymywana, wątek, który przechowuje blokadę, może ponownie uzyskać i zwolnić blokadę. Każdy inny wątek jest zablokowany przed nabyciem blokady i metoda Monitor.Enter czeka, aż blokada zostanie zwolniona. Metoda Enter uzyskuje zwolnioną blokadę. Można również użyć metody Monitor.TryEnter, aby określić czas, podczas którego wątek próbuje uzyskać blokadę. Ponieważ klasa Monitor jest związana z wątkami, wątek, który uzyskał blokadę, musi zwolnić blokadę, wywołując metodę Monitor.Exit.

Można koordynować interakcję wątków, które zablokowują ten sam obiekt przy użyciu metod Monitor.Wait, Monitor.Pulse i Monitor.PulseAll.

Aby uzyskać więcej informacji, zobacz dokumentację interfejsu Monitor API.

Uwaga / Notatka

Użyj instrukcji lock w języku C# i instrukcji SyncLock w języku Visual Basic, aby zsynchronizować dostęp do udostępnionego zasobu zamiast bezpośrednio używać Monitor klasy. Te instrukcje są implementowane przy użyciu metod Enter, Exit oraz bloku try…finally, aby zapewnić, że nabyta blokada jest zawsze zwalniana.

Klasa Mutex

Klasa System.Threading.Mutex , na przykład Monitor, udziela wyłącznego dostępu do udostępnionego zasobu. Użyj jednego z przeciążeń metody Mutex.WaitOne, aby zażądać przejęcia kontroli nad mutexem. Podobnie jak Monitor, Mutex ma powiązanie z wątkiem, a wątek, który nabył mutex, musi go zwolnić, wywołując metodę Mutex.ReleaseMutex.

W przeciwieństwie do Monitor, klasy Mutex można używać do synchronizacji między procesami. W tym celu należy użyć nazwanego mutexu, który jest widoczny w całym systemie operacyjnym. Aby utworzyć nazwane wystąpienie mutex, użyj konstruktora Mutex , który określa nazwę. Możesz również wywołać metodę Mutex.OpenExisting, aby otworzyć istniejący systemowy mutex nazwany.

Aby uzyskać więcej informacji, zobacz artykuł Mutexes oraz dokumentację Mutex API.

Struktura SpinLock

Struktura System.Threading.SpinLock, podobnie jak Monitor, udziela wyłącznego dostępu do udostępnionego zasobu w zależności od dostępności blokady. Podczas SpinLock próby uzyskania blokady, która jest niedostępna, czeka w pętli, wielokrotnie sprawdzając, aż blokada stanie się dostępna.

Aby uzyskać więcej informacji na temat korzyści i wad używania spinlocka, zobacz artykuł SpinLock i odniesienie do SpinLock API.

Klasa ReaderWriterLockSlim

Klasa System.Threading.ReaderWriterLockSlim udziela wyłącznego dostępu do udostępnionego zasobu do zapisu i umożliwia wielu wątkom jednoczesne uzyskiwanie dostępu do zasobu do odczytu. Możesz użyć ReaderWriterLockSlim do zsynchronizowania dostępu do udostępnionej struktury danych, która obsługuje bezpieczne dla wątków operacje odczytu, ale wymaga wyłącznego dostępu do wykonania operacji zapisu. pl-PL: Gdy wątek żąda wyłącznego dostępu (na przykład przez wywołanie metody ReaderWriterLockSlim.EnterWriteLock), kolejne żądania czytelnika i piszącego blokują, dopóki wszyscy istniejący czytelnicy nie opuszczą blokady, a piszący wejdzie i opuści blokadę.

Aby uzyskać więcej informacji, zobacz dokumentację interfejsu ReaderWriterLockSlim API.

Klasy Semaphore i SemaphoreSlim

Klasy System.Threading.Semaphore i System.Threading.SemaphoreSlim ograniczają liczbę wątków, które mogą uzyskiwać dostęp do współużytkowanego zasobu lub puli zasobów jednocześnie. Dodatkowe wątki, które żądają zasobu, czekają, aż jakikolwiek wątek zwolni semafor. Ponieważ semafor nie ma powiązania z wątkiem, jeden wątek może przyjąć semafor, a inny może go zwolnić.

SemaphoreSlim jest lekką alternatywą dla Semaphore i może być używana tylko do synchronizacji w granicach jednego procesu.

W systemie Windows można użyć Semaphore do synchronizacji procesów. W tym celu utwórz wystąpienie Semaphore, które reprezentuje nazwany semafor systemowy, używając jednego z konstruktorów Semaphore określających nazwę lub poprzez zastosowanie metody Semaphore.OpenExisting. SemaphoreSlim nie obsługuje nazwanych semaforów systemowych.

Aby uzyskać więcej informacji, zobacz artykuł Semaphore i SemaphoreSlim oraz odniesienia do Semaphore lub SemaphoreSlim API.

Interakcja między wątkami lub sygnalizowanie

Interakcja wątku (lub sygnalizowanie wątków) oznacza, że wątek musi czekać na powiadomienie lub sygnał z co najmniej jednego wątku, aby kontynuować. Jeśli na przykład wątek A wywołuje metodę Thread.Join wątku B, wątek A zostanie zablokowany do momentu zakończenia wątku B. Prymitywy synchronizacji opisane w poprzedniej sekcji zapewniają inny mechanizm sygnalizowania: zwalniając blokadę, wątek powiadamia inny wątek, że może kontynuować, zdobywając blokadę.

W tej sekcji opisano dodatkowe konstrukcje sygnalizacyjne dostarczane przez platformę .NET.

Klasy EventWaitHandle, AutoResetEvent, ManualResetEvent i ManualResetEventSlim

Klasa System.Threading.EventWaitHandle reprezentuje zdarzenie synchronizacji wątków.

Zdarzenie synchronizacji może być w stanie niesygnalizowanym lub zasygnalizowanym. Gdy stan zdarzenia jest niezasygnalizowany, wątek wywołujący przeciążenie zdarzenia WaitOne jest blokowany do momentu zasygnalizowania zdarzenia. Metoda EventWaitHandle.Set ustawia stan zdarzenia na sygnalizowany.

Zachowanie sygnalizowanego EventWaitHandle zależy od jego trybu resetu:

W systemie Windows można użyć EventWaitHandle do synchronizacji procesów. W tym celu utwórz EventWaitHandle wystąpienie reprezentujące nazwane zdarzenie synchronizacji systemu przy użyciu jednego z konstruktorów EventWaitHandle , które określa nazwę lub metodę EventWaitHandle.OpenExisting .

Aby uzyskać więcej informacji, zobacz artykuł EventWaitHandle . Aby zapoznać się z dokumentacją interfejsu API, zobacz EventWaitHandle, AutoResetEvent, ManualResetEventi ManualResetEventSlim.

Klasa CountdownEvent

Klasa System.Threading.CountdownEvent reprezentuje zdarzenie, które staje się ustawiane, gdy jego liczba wynosi zero. Chociaż CountdownEvent.CurrentCount jest większy niż zero, wątek, który wywołuje CountdownEvent.Wait , jest blokowany. Wywołanie metody CountdownEvent.Signal w celu dekrementacji liczby zdarzeń.

W przeciwieństwie do ManualResetEvent lub ManualResetEventSlim, które można użyć do odblokowania wielu wątków za pomocą sygnału z jednego wątku, można użyć CountdownEvent do odblokowania jednego lub więcej wątków za pomocą sygnałów z wielu wątków.

Aby uzyskać więcej informacji, zobacz artykuł CountdownEvent oraz dokumentację CountdownEvent API.

Klasa bariery

Klasa System.Threading.Barrier reprezentuje barierę wykonywania wątku. Wątek, który wywołuje Barrier.SignalAndWait metodę, sygnalizuje, że dotarł do bariery i czeka, aż inne wątki uczestniczące osiągną barierę. Gdy wszystkie wątki uczestników osiągną barierę, kontynuują, a bariera zostaje zresetowana i może być ponownie użyta.

Można użyć Barrier , gdy co najmniej jeden wątek wymaga wyników innych wątków przed przejściem do następnej fazy obliczeń.

Aby uzyskać więcej informacji, zobacz artykuł Bariera i Barrier dokumentację interfejsu API.

Zablokowana klasa

Klasa System.Threading.Interlocked udostępnia metody statyczne, które wykonują proste operacje atomowe na zmiennych. Te operacje atomowe obejmują dodawanie, inkrementację i dekrementację, wymianę i warunkową wymianę zależną od porównania, oraz operację odczytu wartości 64-bitowej liczby całkowitej.

Aby uzyskać więcej informacji, zobacz dokumentację interfejsu Interlocked API.

Struktura SpinWait

Struktura System.Threading.SpinWait zapewnia obsługę oczekiwania opartego na mechanizmie spinowym. Możesz użyć go, gdy wątek musi poczekać na zasygnaliowanie zdarzenia lub spełnić warunek, ale gdy rzeczywisty czas oczekiwania powinien być krótszy niż czas oczekiwania wymagany przez użycie uchwytu oczekiwania lub przez zablokowanie wątku w inny sposób. Za pomocą polecenia SpinWaitmożna określić krótki okres czasu do uruchomienia podczas oczekiwania, a następnie uzyskać (na przykład przez oczekiwanie lub spanie) tylko wtedy, gdy warunek nie został spełniony w określonym czasie.

Aby uzyskać więcej informacji, zobacz artykuł SpinWait i odniesienie SpinWait API.

Zobacz także