ReaderWriterLockSlim Klasa

Definicja

Reprezentuje blokadę, która służy do zarządzania dostępem do zasobu, umożliwiając wielu wątkom odczyt lub wyłączny dostęp do zapisu.

public ref class ReaderWriterLockSlim : IDisposable
public class ReaderWriterLockSlim : IDisposable
type ReaderWriterLockSlim = class
    interface IDisposable
Public Class ReaderWriterLockSlim
Implements IDisposable
Dziedziczenie
ReaderWriterLockSlim
Implementuje

Przykłady

W poniższym przykładzie pokazano prostą zsynchronizowaną pamięć podręczną zawierającą ciągi z kluczami całkowitymi. Wystąpienie ReaderWriterLockSlim programu służy do synchronizowania dostępu do Dictionary<TKey,TValue> obiektu, który służy jako wewnętrzna pamięć podręczna.

Przykład zawiera proste metody dodawania do pamięci podręcznej, usuwania z pamięci podręcznej i odczytywania z pamięci podręcznej. Aby zademonstrować limity czasu, przykład zawiera metodę, która dodaje do pamięci podręcznej tylko wtedy, gdy może to zrobić w ramach określonego limitu czasu.

Aby zademonstrować tryb uaktualniania, przykład zawiera metodę, która pobiera wartość skojarzona z kluczem i porównuje ją z nową wartością. Jeśli wartość jest niezmieniona, metoda zwraca stan wskazujący brak zmian. Jeśli dla klucza nie zostanie znaleziona żadna wartość, zostanie wstawiona para klucz/wartość. Jeśli wartość została zmieniona, zostanie ona zaktualizowana. Tryb uaktualniania umożliwia wątkowi uaktualnienie z dostępu do odczytu do zapisu zgodnie z potrzebami bez ryzyka zakleszczenia.

Przykład zawiera zagnieżdżone wyliczenie, które określa wartości zwracane dla metody, która demonstruje tryb uaktualniania.

W przykładzie użyto konstruktora bez parametrów do utworzenia blokady, więc rekursja nie jest dozwolona. Programowanie jest ReaderWriterLockSlim prostsze i mniej podatne na błąd, gdy blokada nie zezwala na rekursję.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
public class SynchronizedCache 
{
    private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
    private Dictionary<int, string> innerCache = new Dictionary<int, string>();

    public int Count
    { get { return innerCache.Count; } }

    public string Read(int key)
    {
        cacheLock.EnterReadLock();
        try
        {
            return innerCache[key];
        }
        finally
        {
            cacheLock.ExitReadLock();
        }
    }

    public void Add(int key, string value)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Add(key, value);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public bool AddWithTimeout(int key, string value, int timeout)
    {
        if (cacheLock.TryEnterWriteLock(timeout))
        {
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
            return true;
        }
        else
        {
            return false;
        }
    }

    public AddOrUpdateStatus AddOrUpdate(int key, string value)
    {
        cacheLock.EnterUpgradeableReadLock();
        try
        {
            string result = null;
            if (innerCache.TryGetValue(key, out result))
            {
                if (result == value)
                {
                    return AddOrUpdateStatus.Unchanged;
                }
                else
                {
                    cacheLock.EnterWriteLock();
                    try
                    {
                        innerCache[key] = value;
                    }
                    finally
                    {
                        cacheLock.ExitWriteLock();
                    }
                    return AddOrUpdateStatus.Updated;
                }
            }
            else
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache.Add(key, value);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
                return AddOrUpdateStatus.Added;
            }
        }
        finally
        {
            cacheLock.ExitUpgradeableReadLock();
        }
    }

    public void Delete(int key)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Remove(key);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public enum AddOrUpdateStatus
    {
        Added,
        Updated,
        Unchanged
    };

    ~SynchronizedCache()
    {
       if (cacheLock != null) cacheLock.Dispose();
    }
}
Public Class SynchronizedCache
    Private cacheLock As New ReaderWriterLockSlim()
    Private innerCache As New Dictionary(Of Integer, String)

    Public ReadOnly Property Count As Integer
       Get
          Return innerCache.Count
       End Get
    End Property
    
    Public Function Read(ByVal key As Integer) As String
        cacheLock.EnterReadLock()
        Try
            Return innerCache(key)
        Finally
            cacheLock.ExitReadLock()
        End Try
    End Function

    Public Sub Add(ByVal key As Integer, ByVal value As String)
        cacheLock.EnterWriteLock()
        Try
            innerCache.Add(key, value)
        Finally
            cacheLock.ExitWriteLock()
        End Try
    End Sub

    Public Function AddWithTimeout(ByVal key As Integer, ByVal value As String, _
                                   ByVal timeout As Integer) As Boolean
        If cacheLock.TryEnterWriteLock(timeout) Then
            Try
                innerCache.Add(key, value)
            Finally
                cacheLock.ExitWriteLock()
            End Try
            Return True
        Else
            Return False
        End If
    End Function

    Public Function AddOrUpdate(ByVal key As Integer, _
                                ByVal value As String) As AddOrUpdateStatus
        cacheLock.EnterUpgradeableReadLock()
        Try
            Dim result As String = Nothing
            If innerCache.TryGetValue(key, result) Then
                If result = value Then
                    Return AddOrUpdateStatus.Unchanged
                Else
                    cacheLock.EnterWriteLock()
                    Try
                        innerCache.Item(key) = value
                    Finally
                        cacheLock.ExitWriteLock()
                    End Try
                    Return AddOrUpdateStatus.Updated
                End If
            Else
                cacheLock.EnterWriteLock()
                Try
                    innerCache.Add(key, value)
                Finally
                    cacheLock.ExitWriteLock()
                End Try
                Return AddOrUpdateStatus.Added
            End If
        Finally
            cacheLock.ExitUpgradeableReadLock()
        End Try
    End Function

    Public Sub Delete(ByVal key As Integer)
        cacheLock.EnterWriteLock()
        Try
            innerCache.Remove(key)
        Finally
            cacheLock.ExitWriteLock()
        End Try
    End Sub

    Public Enum AddOrUpdateStatus
        Added
        Updated
        Unchanged
    End Enum

    Protected Overrides Sub Finalize()
       If cacheLock IsNot Nothing Then cacheLock.Dispose()
    End Sub
End Class

Poniższy kod używa SynchronizedCache następnie obiektu do przechowywania słownika nazw warzyw. Tworzy trzy zadania. Pierwszy zapisuje nazwy warzyw przechowywanych w tablicy do SynchronizedCache wystąpienia. Drugie i trzecie zadanie wyświetla nazwy warzyw, pierwszy w kolejności rosnącej (od niskiego indeksu do wysokiego indeksu), drugi w kolejności malejącej. Ostatnie zadanie wyszukuje ciąg "ogórek", a po znalezieniu go wywołuje metodę EnterUpgradeableReadLock , aby zastąpić ciąg "zielony fasola".

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
public class Example
{
   public static void Main()
   {
      var sc = new SynchronizedCache();
      var tasks = new List<Task>();
      int itemsWritten = 0;

      // Execute a writer.
      tasks.Add(Task.Run( () => { String[] vegetables = { "broccoli", "cauliflower",
                                                          "carrot", "sorrel", "baby turnip",
                                                          "beet", "brussel sprout",
                                                          "cabbage", "plantain",
                                                          "spinach", "grape leaves",
                                                          "lime leaves", "corn",
                                                          "radish", "cucumber",
                                                          "raddichio", "lima beans" };
                                  for (int ctr = 1; ctr <= vegetables.Length; ctr++)
                                     sc.Add(ctr, vegetables[ctr - 1]);

                                  itemsWritten = vegetables.Length;
                                  Console.WriteLine("Task {0} wrote {1} items\n",
                                                    Task.CurrentId, itemsWritten);
                                } ));
      // Execute two readers, one to read from first to last and the second from last to first.
      for (int ctr = 0; ctr <= 1; ctr++) {
         bool desc = ctr == 1;
         tasks.Add(Task.Run( () => { int start, last, step;
                                     int items;
                                     do {
                                        String output = String.Empty;
                                        items = sc.Count;
                                        if (! desc) {
                                           start = 1;
                                           step = 1;
                                           last = items;
                                        }
                                        else {
                                           start = items;
                                           step = -1;
                                           last = 1;
                                        }

                                        for (int index = start; desc ? index >= last : index <= last; index += step)
                                           output += String.Format("[{0}] ", sc.Read(index));

                                        Console.WriteLine("Task {0} read {1} items: {2}\n",
                                                          Task.CurrentId, items, output);
                                     } while (items < itemsWritten | itemsWritten == 0);
                             } ));
      }
      // Execute a red/update task.
      tasks.Add(Task.Run( () => { Thread.Sleep(100);
                                  for (int ctr = 1; ctr <= sc.Count; ctr++) {
                                     String value = sc.Read(ctr);
                                     if (value == "cucumber")
                                        if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
                                           Console.WriteLine("Changed 'cucumber' to 'green bean'");
                                  }
                                } ));

      // Wait for all three tasks to complete.
      Task.WaitAll(tasks.ToArray());

      // Display the final contents of the cache.
      Console.WriteLine();
      Console.WriteLine("Values in synchronized cache: ");
      for (int ctr = 1; ctr <= sc.Count; ctr++)
         Console.WriteLine("   {0}: {1}", ctr, sc.Read(ctr));
   }
}
// The example displays the following output:
//    Task 1 read 0 items:
//
//    Task 3 wrote 17 items
//
//
//    Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
//    beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
//    s] [corn] [radish] [cucumber] [raddichio] [lima beans]
//
//    Task 2 read 0 items:
//
//    Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
//    leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
//    aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
//
//    Changed 'cucumber' to 'green bean'
//
//    Values in synchronized cache:
//       1: broccoli
//       2: cauliflower
//       3: carrot
//       4: sorrel
//       5: baby turnip
//       6: beet
//       7: brussel sprout
//       8: cabbage
//       9: plantain
//       10: spinach
//       11: grape leaves
//       12: lime leaves
//       13: corn
//       14: radish
//       15: green bean
//       16: raddichio
//       17: lima beans
Public Module Example
   Public Sub Main()
      Dim sc As New SynchronizedCache()
      Dim tasks As New List(Of Task)
      Dim itemsWritten As Integer
      
      ' Execute a writer.
      tasks.Add(Task.Run( Sub()
                             Dim vegetables() As String = { "broccoli", "cauliflower",
                                                            "carrot", "sorrel", "baby turnip",
                                                            "beet", "brussel sprout",
                                                            "cabbage", "plantain",
                                                            "spinach", "grape leaves",
                                                            "lime leaves", "corn",
                                                            "radish", "cucumber",
                                                            "raddichio", "lima beans" }
                             For ctr As Integer = 1 to vegetables.Length
                                sc.Add(ctr, vegetables(ctr - 1))
                             Next
                             itemsWritten = vegetables.Length
                             Console.WriteLine("Task {0} wrote {1} items{2}",
                                               Task.CurrentId, itemsWritten, vbCrLf)
                          End Sub))
      ' Execute two readers, one to read from first to last and the second from last to first.
      For ctr As Integer = 0 To 1
         Dim flag As Integer = ctr
         tasks.Add(Task.Run( Sub()
                                Dim start, last, stp As Integer
                                Dim items As Integer
                                Do
                                   Dim output As String = String.Empty
                                   items = sc.Count
                                   If flag = 0 Then
                                      start = 1 : stp = 1 : last = items
                                   Else
                                      start = items : stp = -1 : last = 1
                                   End If
                                   For index As Integer = start To last Step stp
                                      output += String.Format("[{0}] ", sc.Read(index))
                                   Next
                                   Console.WriteLine("Task {0} read {1} items: {2}{3}",
                                                           Task.CurrentId, items, output,
                                                           vbCrLf)
                                Loop While items < itemsWritten Or itemsWritten = 0
                             End Sub))
      Next
      ' Execute a red/update task.
      tasks.Add(Task.Run( Sub()
                             For ctr As Integer = 1 To sc.Count
                                Dim value As String = sc.Read(ctr)
                                If value = "cucumber" Then
                                   If sc.AddOrUpdate(ctr, "green bean") <> SynchronizedCache.AddOrUpdateStatus.Unchanged Then
                                      Console.WriteLine("Changed 'cucumber' to 'green bean'")
                                   End If
                                End If
                             Next
                          End Sub ))

      ' Wait for all three tasks to complete.
      Task.WaitAll(tasks.ToArray())

      ' Display the final contents of the cache.
      Console.WriteLine()
      Console.WriteLine("Values in synchronized cache: ")
      For ctr As Integer = 1 To sc.Count
         Console.WriteLine("   {0}: {1}", ctr, sc.Read(ctr))
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task 1 read 0 items:
'
'    Task 3 wrote 17 items
'
'    Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
'    beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
'    s] [corn] [radish] [cucumber] [raddichio] [lima beans]
'
'    Task 2 read 0 items:
'
'    Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
'    leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
'    aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
'
'    Changed 'cucumber' to 'green bean'
'
'    Values in synchronized cache:
'       1: broccoli
'       2: cauliflower
'       3: carrot
'       4: sorrel
'       5: baby turnip
'       6: beet
'       7: brussel sprout
'       8: cabbage
'       9: plantain
'       10: spinach
'       11: grape leaves
'       12: lime leaves
'       13: corn
'       14: radish
'       15: green bean
'       16: raddichio
'       17: lima beans

Uwagi

Służy ReaderWriterLockSlim do ochrony zasobu, który jest odczytywany przez wiele wątków i zapisywany przez jeden wątek naraz. ReaderWriterLockSlim umożliwia używanie wielu wątków w trybie odczytu, umożliwia jeden wątek w trybie zapisu z wyłączną własnością blokady i umożliwia jednemu wątkowi, który ma dostęp do odczytu w trybie odczytu z możliwością uaktualnienia, z którego wątek może zostać uaktualniony do trybu zapisu bez konieczności zrezygnowania z dostępu do odczytu do zasobu.

Uwaga

Domyślnie nowe wystąpienia programu ReaderWriterLockSlim są tworzone z flagą LockRecursionPolicy.NoRecursion i nie zezwalają na rekursję. Te domyślne zasady są zalecane dla wszystkich nowych programowania, ponieważ rekursja wprowadza niepotrzebne komplikacje i sprawia, że kod jest bardziej podatny na zakleszczenia. Aby uprościć migrację z istniejących projektów korzystających z programu Monitor lub ReaderWriterLock, możesz użyć LockRecursionPolicy.SupportsRecursion flagi , aby utworzyć wystąpienia ReaderWriterLockSlim , które zezwalają na rekursję.

Wątek może wprowadzić blokadę w trzech trybach: tryb odczytu, tryb zapisu i tryb odczytu z możliwością uaktualnienia. (W pozostałej części tego tematu "tryb odczytu z możliwością uaktualnienia" jest określany jako "tryb uaktualniania", a wyrażenie "enter x mode" jest używane w preferencjach do dłuższej frazy "enter the lock in x mode".

Niezależnie od zasad rekursji tylko jeden wątek może być w trybie zapisu w dowolnym momencie. Gdy wątek jest w trybie zapisu, żaden inny wątek nie może wejść do blokady w dowolnym trybie. Tylko jeden wątek może być w trybie uaktualniania w dowolnym momencie. Dowolna liczba wątków może być w trybie odczytu i może istnieć jeden wątek w trybie uaktualniania, podczas gdy inne wątki są w trybie odczytu.

Ważne

Ten typ implementuje IDisposable interfejs. Po zakończeniu korzystania z typu należy usunąć go bezpośrednio lub pośrednio. Aby bezpośrednio usunąć typ, wywołaj jego Dispose metodę try/catch w bloku. Aby usunąć go pośrednio, należy użyć konstrukcji językowej, takiej jak using (w języku C#) lub Using (w Visual Basic). Aby uzyskać więcej informacji, zobacz sekcję "Using an Object that Implements IDisposable" (Używanie obiektu implementujące interfejs IDisposable) w temacie interfejsu IDisposable .

ReaderWriterLockSlim zarządza koligacją wątków; oznacza to, że każdy Thread obiekt musi wykonywać własne wywołania metody, aby wprowadzić i zamknąć tryby blokady. Żaden wątek nie może zmienić trybu innego wątku.

Jeśli obiekt ReaderWriterLockSlim nie zezwala na rekursję, wątek, który próbuje wprowadzić blokadę, może zablokować z kilku powodów:

  • Wątek, który próbuje wprowadzić bloki trybu odczytu, jeśli istnieją wątki oczekujące na wejście w tryb zapisu lub jeśli istnieje jeden wątek w trybie zapisu.

    Uwaga

    Blokowanie nowych czytelników, gdy pisarze są w kolejce, to polityka sprawiedliwości blokady, która faworyzuje pisarzy. Obecna polityka sprawiedliwości równoważy sprawiedliwość czytelnikom i pisarzom, aby promować przepływność w najbardziej typowych scenariuszach. Przyszłe wersje .NET Framework mogą wprowadzać nowe zasady sprawiedliwości.

  • Wątek, który próbuje wprowadzić bloki trybu uaktualniania, jeśli istnieje już wątek w trybie uaktualniania, jeśli istnieją wątki oczekujące na przejście do trybu zapisu lub jeśli w trybie zapisu istnieje jeden wątek.

  • Wątek, który próbuje wprowadzić bloki trybu zapisu, jeśli istnieje wątek w dowolnym z trzech trybów.

Uaktualnianie i obniżanie poziomu blokad

Tryb uaktualniania jest przeznaczony dla przypadków, w których wątek zwykle odczytuje z chronionego zasobu, ale może być konieczne zapisanie go w przypadku spełnienia określonego warunku. Wątek ReaderWriterLockSlim , który został wprowadzony w trybie uaktualniania, ma dostęp do odczytu do chronionego zasobu i może uaktualnić do trybu zapisu, wywołując EnterWriteLock metody lub TryEnterWriteLock . Ponieważ w trybie uaktualniania w danym momencie może istnieć tylko jeden wątek, uaktualnianie do trybu zapisu nie może zakleszczeć, gdy rekursja jest niedozwolona, co jest zasadami domyślnymi.

Ważne

Niezależnie od zasad rekursji, wątek, który początkowo wprowadził tryb odczytu, nie może uaktualnić do trybu uaktualniania lub trybu zapisu, ponieważ ten wzorzec tworzy silne prawdopodobieństwo zakleszczeń. Jeśli na przykład dwa wątki w trybie odczytu spróbują wejść w tryb zapisu, zakleszczą się. Tryb uaktualniania został zaprojektowany w celu uniknięcia takich zakleszczeń.

Jeśli istnieją inne wątki w trybie odczytu, wątek, który uaktualnia bloki. Gdy wątek jest zablokowany, inne wątki, które próbują wejść w tryb odczytu, są blokowane. Gdy wszystkie wątki zostały wycofane z trybu odczytu, zablokowany wątek możliwy do uaktualnienia przechodzi w tryb zapisu. Jeśli istnieją inne wątki oczekujące na przejście do trybu zapisu, pozostaną zablokowane, ponieważ pojedynczy wątek w trybie uaktualniania uniemożliwia im uzyskanie wyłącznego dostępu do zasobu.

Gdy wątek w trybie uaktualniania kończy tryb zapisu, inne wątki oczekujące na przejście do trybu odczytu mogą to zrobić, chyba że istnieją wątki oczekujące na przejście do trybu zapisu. Wątek w trybie uaktualniania może uaktualnić i obniżyć poziom na czas nieokreślony, o ile jest to jedyny wątek zapisywany w chronionym zasobie.

Ważne

Jeśli zezwolisz wielu wątkom na przejście w tryb zapisu lub tryb uaktualniania, nie można zezwolić jednemu wątkowi na monopolizację trybu uaktualniania. W przeciwnym razie wątki, które próbują przejść do trybu zapisu bezpośrednio, zostaną zablokowane na czas nieokreślony, a gdy zostaną zablokowane, inne wątki nie będą mogły wejść w tryb odczytu.

Wątek w trybie uaktualniania może obniżyć poziom do trybu odczytu, najpierw wywołując metodę EnterReadLock , a następnie wywołując metodę ExitUpgradeableReadLock . Ten wzorzec obniżania poziomu jest dozwolony dla wszystkich zasad rekursji blokady, nawet NoRecursion.

Po obniżeniu do trybu odczytu wątek nie może ponownie wprowadzić trybu uaktualniania, dopóki nie zostanie wyłączony z trybu odczytu.

Rekursywne wprowadzanie blokady

Można utworzyć obiekt ReaderWriterLockSlim obsługujący cykliczny wpis blokady przy użyciu konstruktora ReaderWriterLockSlim(LockRecursionPolicy) , który określa zasady blokowania i określa wartość LockRecursionPolicy.SupportsRecursion.

Uwaga

Zastosowanie rekursji nie jest zalecane w przypadku nowego rozwoju, ponieważ wprowadza niepotrzebne komplikacje i sprawia, że kod jest bardziej podatny na zakleszczenia.

W przypadku elementu ReaderWriterLockSlim , który umożliwia rekursję, można powiedzieć o trybach, które można wprowadzić wątek:

  • Wątek w trybie odczytu może cyklicznie przechodzić w tryb odczytu, ale nie może wejść w tryb zapisu lub tryb uaktualniania. Jeśli spróbuje to zrobić, LockRecursionException zostanie zgłoszony. Wprowadzanie trybu odczytu, a następnie wprowadzanie trybu zapisu lub tryb uaktualniania jest wzorcem z silnym prawdopodobieństwem zakleszczeń, więc nie jest to dozwolone. Jak wspomniano wcześniej, tryb uaktualniania jest udostępniany w przypadkach, w których konieczne jest uaktualnienie blokady.

  • Wątek w trybie uaktualniania może przechodzić w tryb zapisu i/lub tryb odczytu i może cyklicznie wprowadzać dowolny z trzech trybów. Jednak próba wprowadzenia bloków trybu zapisu, jeśli istnieją inne wątki w trybie odczytu.

  • Wątek w trybie zapisu może przechodzić w tryb odczytu i/lub tryb uaktualniania i może cyklicznie wprowadzać dowolny z trzech trybów.

  • Wątek, który nie wprowadził blokady, może wejść w dowolny tryb. Ta próba może zablokować z tych samych powodów, co próba wprowadzenia blokady niecyklicznej.

Wątek może zamknąć tryby wprowadzone w dowolnej kolejności, o ile zamyka każdy tryb dokładnie tyle razy, ile wszedł w ten tryb. Jeśli wątek próbuje zamknąć tryb zbyt wiele razy lub aby zamknąć tryb, który nie został wprowadzony, SynchronizationLockException jest zgłaszany.

Blokada stanów

Może się okazać przydatne, aby myśleć o blokadzie pod względem jej stanów. Element ReaderWriterLockSlim może znajdować się w jednym z czterech stanów: nie został wprowadzony, odczyt, uaktualnienie i zapis.

  • Nie wprowadzono: w tym stanie żadne wątki nie zostały wprowadzone do blokady (lub wszystkie wątki zamknęły blokadę).

  • Odczyt: W tym stanie co najmniej jeden wątek wprowadził blokadę dostępu do odczytu do chronionego zasobu.

    Uwaga

    Wątek może wprowadzić blokadę w trybie odczytu przy użyciu EnterReadLock metod lub TryEnterReadLock lub przez obniżenie poziomu z trybu uaktualniania.

  • Uaktualnienie: w tym stanie jeden wątek wprowadził blokadę dostępu do odczytu z opcją uaktualnienia do dostępu do zapisu (czyli w trybie uaktualniania), a co najmniej jeden wątek wprowadził blokadę dostępu do odczytu. Nie więcej niż jeden wątek jednocześnie może wprowadzić blokadę z opcją uaktualnienia; dodatkowe wątki, które próbują wejść w tryb uaktualniania, są blokowane.

  • Zapis: w tym stanie jeden wątek wprowadził blokadę dostępu do zapisu do chronionego zasobu. Ten wątek ma wyłączne posiadanie blokady. Każdy inny wątek, który próbuje wprowadzić blokadę z jakiegokolwiek powodu, jest zablokowany.

W poniższej tabeli opisano przejścia między stanami blokady, które nie zezwalają na rekursję, gdy wątek t podejmuje akcję opisaną w kolumnie po lewej stronie. W momencie wykonywania akcji t nie ma trybu. (Specjalny przypadek, w którym t jest w trybie uaktualniania, jest opisany w przypisach dolnych tabeli). W górnym wierszu opisano stan początkowy blokady. Komórki opisują, co się dzieje z wątkiem, i pokazują zmiany stanu blokady w nawiasach.

Nie wprowadzono (N) Odczyt (R) Uaktualnianie (U) Zapis (W)
t wprowadza tryb odczytu t wprowadza wartość (R). t bloki, jeśli wątki oczekują na tryb zapisu; t w przeciwnym razie wprowadza wartość . t bloki, jeśli wątki oczekują na tryb zapisu; t w przeciwnym razie wprowadza wartość . 1 t Bloki.
t wprowadza tryb uaktualniania t enters (U). t blokuje, jeśli wątki oczekują na tryb zapisu lub tryb uaktualniania; w przeciwnym razie wprowadza t wartość (U). t Bloki. t Bloki.
t wprowadza tryb zapisu t wprowadza wartość (W). t Bloki. t Bloki. 2 t Bloki.

1 Jeśli t rozpoczyna się w trybie uaktualniania, przechodzi w tryb odczytu. Ta akcja nigdy nie blokuje. Stan blokady nie zmienia się. (Wątek może następnie przejść na starszą wersję trybu odczytu, zamykając tryb uaktualniania).

2 Jeśli t rozpoczyna się w trybie uaktualniania, blokuje, jeśli istnieją wątki w trybie odczytu. W przeciwnym razie uaktualnia się do trybu zapisu. Stan blokady zmienia się na Zapis (W). Jeśli t bloki, ponieważ istnieją wątki w trybie odczytu, wchodzi w tryb zapisu, gdy tylko ostatni wątek kończy tryb odczytu, nawet jeśli wątek oczekuje na tryb zapisu.

Gdy wystąpi zmiana stanu, ponieważ wątek zamyka blokadę, następny wątek do przebudzenia jest wybierany w następujący sposób:

  • Najpierw wątek, który czeka na tryb zapisu i jest już w trybie uaktualniania (może istnieć co najwyżej jeden taki wątek).

  • W przeciwnym razie wątek, który czeka na tryb zapisu.

  • W przeciwnym razie wątek, który czeka na tryb uaktualniania.

  • W przeciwnym razie wszystkie wątki oczekujące na tryb odczytu.

Kolejnym stanem blokady jest zawsze Zapis (W) w pierwszych dwóch przypadkach i Uaktualnienie (U) w trzecim przypadku, niezależnie od stanu blokady, gdy wątek wyjścia wyzwolił zmianę stanu. W ostatnim przypadku stanem blokady jest Uaktualnienie (U), jeśli istnieje wątek w trybie uaktualniania po zmianie stanu, a w przeciwnym razie odczyt (R) niezależnie od poprzedniego stanu.

Konstruktory

ReaderWriterLockSlim()

Inicjuje ReaderWriterLockSlim nowe wystąpienie klasy z domyślnymi wartościami właściwości.

ReaderWriterLockSlim(LockRecursionPolicy)

Inicjuje ReaderWriterLockSlim nowe wystąpienie klasy, określając zasady rekursji blokady.

Właściwości

CurrentReadCount

Pobiera całkowitą liczbę unikatowych wątków, które wprowadziły blokadę w trybie odczytu.

IsReadLockHeld

Pobiera wartość wskazującą, czy bieżący wątek został wprowadzony w trybie odczytu.

IsUpgradeableReadLockHeld

Pobiera wartość wskazującą, czy bieżący wątek wprowadził blokadę w trybie uaktualniania.

IsWriteLockHeld

Pobiera wartość wskazującą, czy bieżący wątek wprowadził blokadę w trybie zapisu.

RecursionPolicy

Pobiera wartość wskazującą zasady rekursji dla bieżącego ReaderWriterLockSlim obiektu.

RecursiveReadCount

Pobiera liczbę razy bieżący wątek wszedł do blokady w trybie odczytu jako wskazanie rekursji.

RecursiveUpgradeCount

Pobiera liczbę razy bieżący wątek wszedł do blokady w trybie uaktualniania, co wskazuje na rekursję.

RecursiveWriteCount

Pobiera liczbę razy bieżący wątek wszedł do blokady w trybie zapisu jako wskazanie rekursji.

WaitingReadCount

Pobiera łączną liczbę wątków oczekujących na wprowadzenie blokady w trybie odczytu.

WaitingUpgradeCount

Pobiera łączną liczbę wątków oczekujących na wprowadzenie blokady w trybie uaktualniania.

WaitingWriteCount

Pobiera łączną liczbę wątków oczekujących na wprowadzenie blokady w trybie zapisu.

Metody

Dispose()

Zwalnia wszystkie zasoby używane przez bieżące wystąpienie klasy ReaderWriterLockSlim.

EnterReadLock()

Próbuje wprowadzić blokadę w trybie odczytu.

EnterUpgradeableReadLock()

Próbuje wprowadzić blokadę w trybie uaktualnialnym.

EnterWriteLock()

Próbuje wprowadzić blokadę w trybie zapisu.

Equals(Object)

Określa, czy dany obiekt jest taki sam, jak bieżący obiekt.

(Odziedziczone po Object)
ExitReadLock()

Zmniejsza liczbę rekursji dla trybu odczytu i zamyka tryb odczytu, jeśli wynikowa liczba jest równa 0 (zero).

ExitUpgradeableReadLock()

Zmniejsza liczbę rekursji dla trybu możliwego do uaktualnienia i kończy tryb uaktualniania, jeśli wynikowa liczba wynosi 0 (zero).

ExitWriteLock()

Zmniejsza liczbę rekursji dla trybu zapisu i kończy tryb zapisu, jeśli wynikowa liczba to 0 (zero).

GetHashCode()

Służy jako domyślna funkcja skrótu.

(Odziedziczone po Object)
GetType()

Type Pobiera wartość bieżącego wystąpienia.

(Odziedziczone po Object)
MemberwiseClone()

Tworzy płytkią kopię bieżącego Objectelementu .

(Odziedziczone po Object)
ToString()

Zwraca ciąg reprezentujący bieżący obiekt.

(Odziedziczone po Object)
TryEnterReadLock(Int32)

Próbuje wprowadzić blokadę w trybie odczytu z opcjonalnym limitem czasu całkowitego.

TryEnterReadLock(TimeSpan)

Próbuje wprowadzić blokadę w trybie odczytu z opcjonalnym limitem czasu.

TryEnterUpgradeableReadLock(Int32)

Próbuje wprowadzić blokadę w trybie uaktualnialnym z opcjonalnym limitem czasu.

TryEnterUpgradeableReadLock(TimeSpan)

Próbuje wprowadzić blokadę w trybie uaktualnialnym z opcjonalnym limitem czasu.

TryEnterWriteLock(Int32)

Próbuje wprowadzić blokadę w trybie zapisu z opcjonalnym limitem czasu.

TryEnterWriteLock(TimeSpan)

Próbuje wprowadzić blokadę w trybie zapisu z opcjonalnym limitem czasu.

Dotyczy

Bezpieczeństwo wątkowe

Ten typ jest bezpieczny wątkowo.