ReaderWriterLockSlim Класс

Определение

Представляет блокировку, используемую для управления доступом к ресурсу, которая позволяет нескольким потокам производить считывание или получать монопольный доступ на запись.Represents a lock that is used to manage access to a resource, allowing multiple threads for reading or exclusive access for writing.

public ref class ReaderWriterLockSlim : IDisposable
public class ReaderWriterLockSlim : IDisposable
type ReaderWriterLockSlim = class
    interface IDisposable
Public Class ReaderWriterLockSlim
Implements IDisposable
Наследование
ReaderWriterLockSlim
Реализации

Примеры

В следующем примере показан простой синхронизированный кэш, содержащий строки с целочисленными ключами.The following example shows a simple synchronized cache that holds strings with integer keys. Экземпляр ReaderWriterLockSlim используется для синхронизации доступа Dictionary<TKey,TValue> к, который служит внутренним кэшем.An instance of ReaderWriterLockSlim is used to synchronize access to the Dictionary<TKey,TValue> that serves as the inner cache.

Пример включает простые методы для добавления в кэш, удаления из кэша и чтения из кэша.The example includes simple methods to add to the cache, delete from the cache, and read from the cache. Чтобы продемонстрировать время ожидания, пример включает метод, который добавляет в кэш только в том случае, если это возможно в течение заданного времени ожидания.To demonstrate time-outs, the example includes a method that adds to the cache only if it can do so within a specified time-out.

Чтобы продемонстрировать обновляемый режим, в примере используется метод, который получает значение, связанное с ключом, и сравнивает его с новым значением.To demonstrate upgradeable mode, the example includes a method that retrieves the value associated with a key and compares it with a new value. Если значение не изменилось, метод возвращает состояние, указывающее на отсутствие изменений.If the value is unchanged, the method returns a status indicating no change. Если для ключа не найдено значение, вставляется пара ключ/значение.If no value is found for the key, the key/value pair is inserted. Если значение изменилось, оно обновляется.If the value has changed, it is updated. Обновляемый режим позволяет потоку обновить доступ на чтение для записи при необходимости без риска взаимоблокировок.Upgradeable mode allows the thread to upgrade from read access to write access as needed, without the risk of deadlocks.

Пример включает вложенное перечисление, которое указывает возвращаемые значения для метода, который демонстрирует обновляемый режим.The example includes a nested enumeration that specifies the return values for the method that demonstrates upgradeable mode.

В примере используется конструктор без параметров для создания блокировки, поэтому рекурсия не разрешена.The example uses the parameterless constructor to create the lock, so recursion is not allowed. Программирование на ReaderWriterLockSlim является более простым и менее подверженным ошибкам, если блокировка не допускает рекурсии.Programming the ReaderWriterLockSlim is simpler and less prone to error when the lock does not allow recursion.

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

Следующий код использует SynchronizedCache объект для хранения словаря имен овощ.The following code then uses the SynchronizedCache object to store a dictionary of vegetable names. Он создает три задачи.It creates three tasks. Первый объект записывает имена овощей, хранящиеся в массиве, SynchronizedCache в экземпляр.The first writes the names of vegetables stored in an array to a SynchronizedCache instance. Вторая и третья задачи отображают имена овощей, первый в возрастающем порядке (от нижнего индекса до верхнего индекса), второй в убывающем порядке.The second and third task display the names of the vegetables, the first in ascending order (from low index to high index), the second in descending order. Последняя задача выполняет поиск строки "Cucumber" и при ее обнаружении вызывает EnterUpgradeableReadLock метод для замены строки "Green Bean".The final task searches for the string "cucumber" and, when it finds it, calls the EnterUpgradeableReadLock method to substitute the string "green bean".

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 = Convert.ToBoolean(ctr);
         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

Комментарии

Используется ReaderWriterLockSlim для защиты ресурса, который считывается несколькими потоками и записывается в один поток за раз.Use ReaderWriterLockSlim to protect a resource that is read by multiple threads and written to by one thread at a time. ReaderWriterLockSlimпозволяет нескольким потокам находиться в режиме чтения, что позволяет одному потоку находиться в режиме записи с монопольным владельцем блокировки, а также допускает, чтобы один поток с доступом на чтение набыть в обновляемом режиме чтения, из которого поток может обновиться до режима записи, не требуя его необходимости. доступ на чтение к ресурсу.ReaderWriterLockSlim allows multiple threads to be in read mode, allows one thread to be in write mode with exclusive ownership of the lock, and allows one thread that has read access to be in upgradeable read mode, from which the thread can upgrade to write mode without having to relinquish its read access to the resource.

Примечание

ReaderWriterLockSlim действует так же, как и ReaderWriterLock, но с более простыми правилами рекурсии и изменения состояния блокировки.ReaderWriterLockSlim is similar to ReaderWriterLock, but it has simplified rules for recursion and for upgrading and downgrading lock state. ReaderWriterLockSlim позволяет избежать многих ситуаций взаимоблокировки.ReaderWriterLockSlim avoids many cases of potential deadlock. Кроме того, производительность ReaderWriterLockSlim значительно выше, чем у ReaderWriterLock.In addition, the performance of ReaderWriterLockSlim is significantly better than ReaderWriterLock. Мы рекомендуем применять ReaderWriterLockSlim при любых новых разработках.ReaderWriterLockSlim is recommended for all new development.

По умолчанию новые экземпляры ReaderWriterLockSlim создаются LockRecursionPolicy.NoRecursion с флагом и не допускают рекурсии.By default, new instances of ReaderWriterLockSlim are created with the LockRecursionPolicy.NoRecursion flag and do not allow recursion. Эта политика по умолчанию рекомендуется для всех новых приложений, так как рекурсия вносит ненужные сложности и делает код более подверженным взаимоблокировкам.This default policy is recommended for all new development, because recursion introduces unnecessary complications and makes your code more prone to deadlocks. Чтобы упростить миграцию из существующих проектов, Monitor использующих LockRecursionPolicy.SupportsRecursion или ReaderWriterLock, можно использовать флаг для создания экземпляров ReaderWriterLockSlim , разрешающих рекурсию.To simplify migration from existing projects that use Monitor or ReaderWriterLock, you can use the LockRecursionPolicy.SupportsRecursion flag to create instances of ReaderWriterLockSlim that allow recursion.

Поток может войти в блокировку в трех режимах: режим чтения, режим записи и обновляемый режим чтения.A thread can enter the lock in three modes: read mode, write mode, and upgradeable read mode. (В оставшейся части этого раздела «обновляемый режим чтения» называется "обновляемым режимом", а фраза "режим ввода x " используется в качестве предпочтений более длинной фразе "введите блокировку в x режиме".)(In the rest of this topic, "upgradeable read mode" is referred to as "upgradeable mode", and the phrase "enter x mode" is used in preference to the longer phrase "enter the lock in x mode".)

Независимо от политики рекурсии в любое время может находиться только один поток в режиме записи.Regardless of recursion policy, only one thread can be in write mode at any time. Если поток находится в режиме записи, никакой другой поток не может войти в блокировку в любом режиме.When a thread is in write mode, no other thread can enter the lock in any mode. В любой момент времени в обновляемом режиме может находиться только один поток.Only one thread can be in upgradeable mode at any time. Любое количество потоков может находиться в режиме чтения, и в обновляемом режиме может быть один поток, а другие потоки находятся в режиме чтения.Any number of threads can be in read mode, and there can be one thread in upgradeable mode while other threads are in read mode.

Важно!

Этот тип реализует интерфейс IDisposable.This type implements the IDisposable interface. По окончании использования выдаленную ему память следует прямо или косвенно освободить.When you have finished using the type, you should dispose of it either directly or indirectly. Чтобы сделать это прямо, вызовите его метод Dispose в блоке try/catch.To dispose of the type directly, call its Dispose method in a try/catch block. Чтобы сделать это косвенно, используйте языковые конструкции, такие как using (в C#) или Using (в Visual Basic).To dispose of it indirectly, use a language construct such as using (in C#) or Using (in Visual Basic). Дополнительные сведения см. в разделе "Использование объекта, реализующего IDisposable" в статье об интерфейсе IDisposable.For more information, see the "Using an Object that Implements IDisposable" section in the IDisposable interface topic.

ReaderWriterLockSlimимеет сходство управляемого потока; то есть каждый Thread объект должен выполнять собственные вызовы методов для входа и выхода из режимов блокировки.ReaderWriterLockSlim has managed thread affinity; that is, each Thread object must make its own method calls to enter and exit lock modes. Ни один поток не может изменить режим другого потока.No thread can change the mode of another thread.

ReaderWriterLockSlim Если не допускает рекурсии, поток, пытающийся войти в блокировку, может блокироваться по нескольким причинам:If a ReaderWriterLockSlim does not allow recursion, a thread that tries to enter the lock can block for several reasons:

  • Поток, пытающийся войти в блоки режима чтения, если есть потоки, ожидающие входа в режим записи или выполняющие один поток в режиме записи.A thread that tries to enter read mode blocks if there are threads waiting to enter write mode or if there is a single thread in write mode.

    Примечание

    Блокирование новых читателей, когда модули записи помещаются в очередь, — это политика равноправия блокировок, которая дает предпочтение модулям записи.Blocking new readers when writers are queued is a lock fairness policy that favors writers. Текущая политика равноправия равномерно распределяется между модулями чтения и записи, чтобы повысить пропускную способность в самых распространенных сценариях.The current fairness policy balances fairness to readers and writers, to promote throughput in the most common scenarios. В .NET Framework.NET Framework будущих версиях служб могут приникать новые политики равноправия.Future versions of the .NET Framework.NET Framework may introduce new fairness policies.

  • Поток, пытающийся войти в блоки обновляемого режима, если в обновляемом режиме уже имеется поток, если имеются потоки, ожидающие входа в режим записи, или если в режиме записи имеется один поток.A thread that tries to enter upgradeable mode blocks if there is already a thread in upgradeable mode, if there are threads waiting to enter write mode, or if there is a single thread in write mode.

  • Поток, который пытается войти в режим записи, блокируется, если имеется поток в любом из трех режимов.A thread that tries to enter write mode blocks if there is a thread in any of the three modes.

Обновление и понижение уровня блокировкиUpgrading and Downgrading Locks

Обновляемый режим предназначен для случаев, когда поток обычно считывает данные из защищенного ресурса, но при выполнении какого-либо условия может потребоваться запись в него.Upgradeable mode is intended for cases where a thread usually reads from the protected resource, but might need to write to it if some condition is met. Поток, который вошел ReaderWriterLockSlim в обновляемый режим, имеет доступ на чтение к защищенному ресурсу и может обновить его до режима записи, EnterWriteLock вызвав методы или. TryEnterWriteLockA thread that has entered a ReaderWriterLockSlim in upgradeable mode has read access to the protected resource, and can upgrade to write mode by calling the EnterWriteLock or TryEnterWriteLock methods. Так как в обновляемом режиме может быть только один поток, обновление до режима записи не может быть взаимоблокировками, если рекурсия не разрешена, что является политикой по умолчанию.Because there can be only one thread in upgradeable mode at a time, upgrading to write mode cannot deadlock when recursion is not allowed, which is the default policy.

Важно!

Независимо от политики рекурсии потоку, который изначально перешел в режим чтения, запрещено обновление до обновляемого режима или режима записи, поскольку этот шаблон создает строгую вероятность взаимоблокировок.Regardless of recursion policy, a thread that initially entered read mode is not allowed to upgrade to upgradeable mode or write mode, because that pattern creates a strong probability of deadlocks. Например, если два потока в режиме чтения пытаются войти в режим записи, они будут взаимоблокировками.For example, if two threads in read mode both try to enter write mode, they will deadlock. Обновляемый режим предназначен для предотвращения подобных взаимоблокировок.Upgradeable mode is designed to avoid such deadlocks.

Если в режиме чтения есть другие потоки, то поток, который выполняет обновление, блокируется.If there are other threads in read mode, the thread that is upgrading blocks. Пока поток заблокирован, другие потоки, пытающиеся войти в режим чтения, блокируются.While the thread is blocked, other threads that try to enter read mode are blocked. Когда все потоки вышли из режима чтения, заблокированный обновляемый поток переходит в режим записи.When all threads have exited from read mode, the blocked upgradeable thread enters write mode. Если существуют другие потоки, ожидающие входа в режим записи, они остаются заблокированными, так как один поток, находящихся в обновляемом режиме, не позволяет им получить монопольный доступ к ресурсу.If there are other threads waiting to enter write mode, they remain blocked, because the single thread that is in upgradeable mode prevents them from gaining exclusive access to the resource.

Когда поток в обновляемом режиме выходит из режима записи, другие потоки, ожидающие входа в режим чтения, могут сделать это, если нет потоков, ожидающих входа в режим записи.When the thread in upgradeable mode exits write mode, other threads that are waiting to enter read mode can do so, unless there are threads waiting to enter write mode. Поток в обновляемом режиме может обновляться и переходить на более раннюю версию неограниченно, пока это единственный поток, записывающий данные в защищенный ресурс.The thread in upgradeable mode can upgrade and downgrade indefinitely, as long as it is the only thread that writes to the protected resource.

Важно!

Если разрешить нескольким потокам переход в режим записи или обновляемый режим, то не следует разрешать одному потоку монопольно использовать обновляемый режим.If you allow multiple threads to enter write mode or upgradeable mode, you must not allow one thread to monopolize upgradeable mode. В противном случае потоки, которые пытаются войти в режим записи напрямую, будут заблокированы на неопределенное время, и пока они будут заблокированы, другие потоки не смогут войти в режим чтения.Otherwise, threads that try to enter write mode directly will be blocked indefinitely, and while they are blocked, other threads will be unable to enter read mode.

Поток в обновляемом режиме можно понизить до режима чтения, сначала вызвав EnterReadLock метод, а затем ExitUpgradeableReadLock вызвав метод.A thread in upgradeable mode can downgrade to read mode by first calling the EnterReadLock method and then calling the ExitUpgradeableReadLock method. Этот шаблон перехода на более раннюю версию разрешен для всех политик рекурсии блокировки NoRecursion, даже.This downgrade pattern is allowed for all lock recursion policies, even NoRecursion.

После перехода в режим чтения поток не может повторно войти в обновляемый режим, пока он не завершит работу из режима чтения.After downgrading to read mode, a thread cannot reenter upgradeable mode until it has exited from read mode.

Рекурсивный вход в блокировкуEntering the Lock Recursively

Можно создать ReaderWriterLockSlim , поддерживающий запись рекурсивной блокировки с ReaderWriterLockSlim(LockRecursionPolicy) помощью конструктора, определяющего политику блокировки, и указав LockRecursionPolicy.SupportsRecursion.You can create a ReaderWriterLockSlim that supports recursive lock entry by using the ReaderWriterLockSlim(LockRecursionPolicy) constructor that specifies lock policy, and specifying LockRecursionPolicy.SupportsRecursion.

Примечание

Использование рекурсии не рекомендуется для новых разработок, поскольку оно создает ненужные сложности и делает код более подверженным взаимоблокировкам.The use of recursion is not recommended for new development, because it introduces unnecessary complications and makes your code more prone to deadlocks.

ReaderWriterLockSlim Для, допускающего рекурсию, можно сказать о режимах, которые поток может ввести:For a ReaderWriterLockSlim that allows recursion, the following can be said about the modes a thread can enter:

  • Поток в режиме чтения может войти в режим чтения рекурсивно, но не может войти в режим записи или обновляемый режим.A thread in read mode can enter read mode recursively, but cannot enter write mode or upgradeable mode. При попытке сделать это LockRecursionException возникает исключение.If it tries to do this, a LockRecursionException is thrown. Переход в режим чтения и переход в режим записи или обновляемый режим — это шаблон с высокой вероятностью взаимоблокировок, поэтому он не разрешен.Entering read mode and then entering write mode or upgradeable mode is a pattern with a strong probability of deadlocks, so it is not allowed. Как обсуждалось ранее, обновляемый режим предоставляется для случаев, когда требуется обновить блокировку.As discussed earlier, upgradeable mode is provided for cases where it is necessary to upgrade a lock.

  • Поток в обновляемом режиме может входить в режим записи или чтения, а также может вводить любой из трех режимов рекурсивно.A thread in upgradeable mode can enter write mode and/or read mode, and can enter any of the three modes recursively. Однако попытка входа в режим записи блокируется, если в режиме чтения есть другие потоки.However, an attempt to enter write mode blocks if there are other threads in read mode.

  • Поток в режиме записи может входить в режим чтения и (или) обновляемый режим, а также может вводить любой из трех режимов рекурсивно.A thread in write mode can enter read mode and/or upgradeable mode, and can enter any of the three modes recursively.

  • Поток, который не вошел в блокировку, может войти в любой режим.A thread that has not entered the lock can enter any mode. Эта попытка может блокироваться по тем же причинам, что и попытка входа в нерекурсивную блокировку.This attempt can block for the same reasons as an attempt to enter a non-recursive lock.

Поток может выйти из режимов, которые он указал в любом порядке, при условии, что каждый режим завершается точно так же, как если бы он был указан в этом режиме.A thread can exit the modes it has entered in any order, as long as it exits each mode exactly as many times as it entered that mode. Если поток пытается выйти из режима слишком много раз или выйти из режима, который он не указал, SynchronizationLockException создается исключение.If a thread tries to exit a mode too many times, or to exit a mode it has not entered, a SynchronizationLockException is thrown.

Состояния блокировкиLock States

Может оказаться полезным рассматривать блокировку с точки зрения ее состояний.You may find it useful to think of the lock in terms of its states. ReaderWriterLockSlim Может находиться в одном из четырех состояний: не указано, чтение, обновление и запись.A ReaderWriterLockSlim can be in one of four states: not entered, read, upgrade, and write.

  • Не указано: В этом состоянии потоки не вошел в блокировку (или все потоки вышли из блокировки).Not entered: In this state, no threads have entered the lock (or all threads have exited the lock).

  • Просмотр В этом состоянии один или несколько потоков вошел в блокировку на доступ для чтения к защищенному ресурсу.Read: In this state, one or more threads have entered the lock for read access to the protected resource.

    Примечание

    Поток может войти в блокировку в режиме чтения с помощью EnterReadLock методов или TryEnterReadLock или перехода на более раннюю версию из обновляемого режима.A thread can enter the lock in read mode by using the EnterReadLock or TryEnterReadLock methods, or by downgrading from upgradeable mode.

  • Обновления В этом состоянии один поток вошел в блокировку для доступа на чтение с возможностью обновления до доступа на запись (то есть в обновляемом режиме), а ноль или более потоков вошел в блокировку для доступа на чтение.Upgrade: In this state, one thread has entered the lock for read access with the option to upgrade to write access (that is, in upgradeable mode), and zero or more threads have entered the lock for read access. Ни один поток за раз может войти в блокировку с возможностью обновления. дополнительные потоки, пытающиеся войти в обновляемый режим, блокируются.No more than one thread at a time can enter the lock with the option to upgrade; additional threads that try to enter upgradeable mode are blocked.

  • Будет В этом состоянии один поток вошел в блокировку для доступа на запись к защищенному ресурсу.Write: In this state, one thread has entered the lock for write access to the protected resource. Этот поток монопольно владеет блокировкой.That thread has exclusive possession of the lock. Любой другой поток, пытающийся войти в блокировку по любой причине, блокируется.Any other thread that tries to enter the lock for any reason is blocked.

В следующей таблице описаны переходы между состояниями блокировки для блокировок, которые не допускают рекурсии, когда t поток выполняет действие, описанное в крайнем левом столбце.The following table describes the transitions between lock states, for locks that do not allow recursion, when a thread t takes the action described in the leftmost column. В то время, когда действие выполняется, t не имеет режима.At the time it takes the action, t has no mode. (Особый случай, когда t находится в обновляемом режиме, описывается в сносках таблицы.) В верхней строке описывается начальное состояние блокировки.(The special case where t is in upgradeable mode is described in the table footnotes.) The top row describes the starting state of the lock. Ячейки описывают, что происходит с потоком, и отображают изменения состояния блокировки в круглых скобках.The cells describe what happens to the thread, and show changes to the lock state in parentheses.

Не задано (N)Not entered (N) Чтение (R)Read (R) Обновление (U)Upgrade (U) Запись (W)Write (W)
tпереходит в режим чтенияt enters read mode tвводит (R).t enters (R). tблокируется, если потоки ожидают режима записи; в противном случае вводит. tt blocks if threads are waiting for write mode; otherwise, t enters. tблокируется, если потоки ожидают режима записи; в противном случае вводит. t 1t blocks if threads are waiting for write mode; otherwise, t enters.1 tблоков.t blocks.
tпереходит в обновляемый режимt enters upgradeable mode tвводит (U).t enters (U). tблокируется, если потоки ожидают режима записи или обновления. в противном случае вводит (U). tt blocks if threads are waiting for write mode or upgrade mode; otherwise, t enters (U). tблоков.t blocks. tблоков.t blocks.
tвходит в режим записиt enters write mode tвводит (W).t enters (W). tблоков.t blocks. tблоков. 2t blocks.2 tблоков.t blocks.

1 если t запускается в обновляемом режиме, он переходит в режим чтения.1 If t starts out in upgradeable mode, it enters read mode. Это действие никогда не блокируется.This action never blocks. Состояние блокировки не изменяется.The lock state does not change. (Поток может выполнить переход на более раннюю версию в режиме чтения путем выхода из обновляемого режима.)(The thread can then complete a downgrade to read mode by exiting upgradeable mode.)

2 если t запускается в обновляемом режиме, он блокируется при наличии потоков в режиме чтения.2 If t starts out in upgradeable mode, it blocks if there are threads in read mode. В противном случае он обновляется до режима записи.Otherwise it upgrades to write mode. Изменение состояния блокировки на запись (W).The lock state changes to Write (W). Если t блоки блокируются из-за наличия потоков в режиме чтения, он переходит в режим записи, как только последний поток выходит из режима чтения, даже если имеются потоки, ожидающие входа в режим записи.If t blocks because there are threads in read mode, it enters write mode as soon as the last thread exits read mode, even if there are threads waiting to enter write mode.

Когда происходит изменение состояния, поскольку поток завершает блокировку, следующий поток для пробуждения выбирается следующим образом:When a state change occurs because a thread exits the lock, the next thread to be awakened is selected as follows:

  • Во-первых, поток, который ожидает режима записи и уже находится в обновляемом режиме (может существовать не более одного такого потока).First, a thread that is waiting for write mode and is already in upgradeable mode (there can be at most one such thread).

  • Сбой этого потока, ожидающего режима записи.Failing that, a thread that is waiting for write mode.

  • Сбой этого потока, ожидающего обновляемого режима.Failing that, a thread that is waiting for upgradeable mode.

  • В таком случае все потоки ожидают режима чтения.Failing that, all threads that are waiting for read mode.

Последующее состояние блокировки всегда пишется (W) в первых двух случаях и обновляется (U) в третьем случае, независимо от состояния блокировки, когда поток выхода инициировал изменение состояния.The subsequent state of the lock is always Write (W) in the first two cases and Upgrade (U) in the third case, regardless of the state of the lock when the exiting thread triggered the state change. В последнем случае состояние блокировки — Upgrade (U), если после изменения состояния поток находится в обновляемом режиме, а в противном случае — Read (R), независимо от предыдущего состояния.In the last case, the state of the lock is Upgrade (U) if there is a thread in upgradeable mode after the state change, and Read (R) otherwise, regardless of the prior state.

Конструкторы

ReaderWriterLockSlim()

Инициализирует новый экземпляр класса ReaderWriterLockSlim значениями свойств по умолчанию.Initializes a new instance of the ReaderWriterLockSlim class with default property values.

ReaderWriterLockSlim(LockRecursionPolicy)

Инициализирует новый экземпляр класса ReaderWriterLockSlim с указанием политики рекурсии блокировки.Initializes a new instance of the ReaderWriterLockSlim class, specifying the lock recursion policy.

Свойства

CurrentReadCount

Получает общее количество уникальных потоков, вошедших в блокировку в режиме чтения.Gets the total number of unique threads that have entered the lock in read mode.

IsReadLockHeld

Возвращает значение, указывающее, вошел ли текущий поток в блокировку в режиме чтения.Gets a value that indicates whether the current thread has entered the lock in read mode.

IsUpgradeableReadLockHeld

Возвращает значение, указывающее, вошел ли текущий поток в блокировку в обновляемом режиме.Gets a value that indicates whether the current thread has entered the lock in upgradeable mode.

IsWriteLockHeld

Возвращает значение, указывающее, вошел ли текущий поток в блокировку в режиме записи.Gets a value that indicates whether the current thread has entered the lock in write mode.

RecursionPolicy

Возвращает значение, указывающее политику рекурсии для текущего объекта ReaderWriterLockSlim.Gets a value that indicates the recursion policy for the current ReaderWriterLockSlim object.

RecursiveReadCount

Возвращает количество раз, которые текущий поток входил в блокировку в режиме чтения, как показатель рекурсии.Gets the number of times the current thread has entered the lock in read mode, as an indication of recursion.

RecursiveUpgradeCount

Возвращает количество раз, которые текущий поток входил в блокировку в обновляемом режиме, как показатель рекурсии.Gets the number of times the current thread has entered the lock in upgradeable mode, as an indication of recursion.

RecursiveWriteCount

Возвращает количество раз, которые текущий поток входил в блокировку в режиме записи, как показатель рекурсии.Gets the number of times the current thread has entered the lock in write mode, as an indication of recursion.

WaitingReadCount

Возвращает общее количество потоков, ожидающих вхождения в блокировку в режиме чтения.Gets the total number of threads that are waiting to enter the lock in read mode.

WaitingUpgradeCount

Возвращает общее количество потоков, ожидающих входа в блокировку в обновляемом режиме.Gets the total number of threads that are waiting to enter the lock in upgradeable mode.

WaitingWriteCount

Возвращает общее количество потоков, ожидающих входа в блокировку в режиме записи.Gets the total number of threads that are waiting to enter the lock in write mode.

Методы

Dispose()

Освобождает все ресурсы, используемые текущим экземпляром класса ReaderWriterLockSlim.Releases all resources used by the current instance of the ReaderWriterLockSlim class.

EnterReadLock()

Пытается выполнить вход в блокировку в режиме чтения.Tries to enter the lock in read mode.

EnterUpgradeableReadLock()

Пытается выполнить вход в блокировку в обновляемом режиме.Tries to enter the lock in upgradeable mode.

EnterWriteLock()

Пытается выполнить вход в блокировку в режиме записи.Tries to enter the lock in write mode.

Equals(Object)

Определяет, равен ли заданный объект текущему объекту.Determines whether the specified object is equal to the current object.

(Унаследовано от Object)
ExitReadLock()

Уменьшает счетчик глубины рекурсии для режима чтения, и выходит из режима чтения, если значение счетчик принял значение 0 (нуль).Reduces the recursion count for read mode, and exits read mode if the resulting count is 0 (zero).

ExitUpgradeableReadLock()

Уменьшает счетчик глубины рекурсии для обновляемого режима, и выходит из обновляемого режима, если счетчик принял значение 0 (нуль).Reduces the recursion count for upgradeable mode, and exits upgradeable mode if the resulting count is 0 (zero).

ExitWriteLock()

Уменьшает счетчик глубины рекурсии для режима записи, и выходит из режима записи, если счетчик принял значение 0 (нуль).Reduces the recursion count for write mode, and exits write mode if the resulting count is 0 (zero).

GetHashCode()

Служит хэш-функцией по умолчанию.Serves as the default hash function.

(Унаследовано от Object)
GetType()

Возвращает объект Type для текущего экземпляра.Gets the Type of the current instance.

(Унаследовано от Object)
MemberwiseClone()

Создает неполную копию текущего объекта Object.Creates a shallow copy of the current Object.

(Унаследовано от Object)
ToString()

Возвращает строку, представляющую текущий объект.Returns a string that represents the current object.

(Унаследовано от Object)
TryEnterReadLock(Int32)

Пытается войти в блокировку в режиме чтения с необязательным указанием времени ожидания целым числом.Tries to enter the lock in read mode, with an optional integer time-out.

TryEnterReadLock(TimeSpan)

Пытается войти в блокировку в режиме чтения с необязательным указанием времени ожидания.Tries to enter the lock in read mode, with an optional time-out.

TryEnterUpgradeableReadLock(Int32)

Пытается войти в блокировку в обновляемом режиме с необязательным указанием времени ожидания.Tries to enter the lock in upgradeable mode, with an optional time-out.

TryEnterUpgradeableReadLock(TimeSpan)

Пытается войти в блокировку в обновляемом режиме с необязательным указанием времени ожидания.Tries to enter the lock in upgradeable mode, with an optional time-out.

TryEnterWriteLock(Int32)

Пытается войти в блокировку в режиме записи с необязательным указанием времени ожидания.Tries to enter the lock in write mode, with an optional time-out.

TryEnterWriteLock(TimeSpan)

Пытается войти в блокировку в режиме записи с необязательным указанием времени ожидания.Tries to enter the lock in write mode, with an optional time-out.

Применяется к

Потокобезопасность

Данный тип потокобезопасен.This type is thread safe.