Este artículo se tradujo automáticamente. Para ver el artículo en inglés, active la casilla Inglés. Además, puede mostrar el texto en inglés en una ventana emergente si mueve el puntero del mouse sobre el texto.
Traducción
Inglés

Clase ReaderWriterLockSlim

 

Publicado: octubre de 2016

Representa un bloqueo que se utiliza para administrar el acceso a un recurso y que permite varios subprocesos para la lectura o acceso exclusivo para la escritura.

Espacio de nombres:   System.Threading
Ensamblado:  System.Core (en System.Core.dll)

System.Object
  System.Threading.ReaderWriterLockSlim

[HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, 
	ExternalThreading = true)]
[HostProtectionAttribute(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
public class ReaderWriterLockSlim : IDisposable

NombreDescripción
System_CAPS_pubmethodReaderWriterLockSlim()

Inicializa una nueva instancia de la clase ReaderWriterLockSlim con los valores de propiedad predeterminados.

System_CAPS_pubmethodReaderWriterLockSlim(LockRecursionPolicy)

Inicializa una nueva instancia de la clase ReaderWriterLockSlim especificando la directiva de recursividad de bloqueo.

NombreDescripción
System_CAPS_pubpropertyCurrentReadCount

Obtiene el número total de subprocesos únicos que han entrado en el bloqueo en modo de lectura.

System_CAPS_pubpropertyIsReadLockHeld

Obtiene un valor que indica si el subproceso actual ha entrado en el bloqueo en modo de lectura.

System_CAPS_pubpropertyIsUpgradeableReadLockHeld

Obtiene un valor que indica si el subproceso actual entró en el bloqueo en modo de actualización.

System_CAPS_pubpropertyIsWriteLockHeld

Obtiene un valor que indica si el subproceso actual ha entrado en el bloqueo en modo de escritura.

System_CAPS_pubpropertyRecursionPolicy

Obtiene un valor que indica la directiva de recursividad del objeto ReaderWriterLockSlim actual.

System_CAPS_pubpropertyRecursiveReadCount

Obtiene el número de veces que el subproceso actual ha entrado en el bloqueo en modo de lectura, como una indicación de recursividad.

System_CAPS_pubpropertyRecursiveUpgradeCount

Obtiene el número de veces que el subproceso actual ha entrado en el bloqueo en modo de actualización, como una indicación de recursividad.

System_CAPS_pubpropertyRecursiveWriteCount

Obtiene el número de veces que el subproceso actual ha entrado en el bloqueo en modo de escritura, como una indicación de recursividad.

System_CAPS_pubpropertyWaitingReadCount

Obtiene el número total de subprocesos que están a la espera de entrar en el bloqueo en modo de lectura.

System_CAPS_pubpropertyWaitingUpgradeCount

Obtiene el número total de subprocesos que están a la espera de entrar en el bloqueo en modo de actualización.

System_CAPS_pubpropertyWaitingWriteCount

Obtiene el número total de subprocesos que están a la espera de entrar en el bloqueo en modo de escritura.

NombreDescripción
System_CAPS_pubmethodDispose()

Libera todos los recursos usados por la instancia actual de la clase ReaderWriterLockSlim.

System_CAPS_pubmethodEnterReadLock()

Intenta entrar en el bloqueo en modo de lectura.

System_CAPS_pubmethodEnterUpgradeableReadLock()

Intenta entrar en el bloqueo en modo de actualización.

System_CAPS_pubmethodEnterWriteLock()

Intenta entrar en el bloqueo en modo de escritura.

System_CAPS_pubmethodEquals(Object)

Determina si el objeto especificado es igual al objeto actual.(Heredado de Object).

System_CAPS_pubmethodExitReadLock()

Reduce el recuento de recursividad para el modo de lectura y sale del modo de lectura si el recuento resultante es 0 (cero).

System_CAPS_pubmethodExitUpgradeableReadLock()

Reduce el recuento de recursividad para el modo de actualización y sale del modo de actualización si el recuento resultante es 0 (cero).

System_CAPS_pubmethodExitWriteLock()

Reduce el recuento de recursividad para el modo de escritura y sale del modo de escritura si el recuento resultante es 0 (cero).

System_CAPS_protmethodFinalize()

Permite que un objeto intente liberar recursos y realizar otras operaciones de limpieza antes de ser reclamado por el recolector de basura. (Heredado de Object).

System_CAPS_pubmethodGetHashCode()

Sirve como la función hash predeterminada.(Heredado de Object).

System_CAPS_pubmethodGetType()

Obtiene el Type de la instancia actual.(Heredado de Object).

System_CAPS_protmethodMemberwiseClone()

Crea una copia superficial del Object actual.(Heredado de Object).

System_CAPS_pubmethodToString()

Devuelve una cadena que representa al objeto actual. (Heredado de Object).

System_CAPS_pubmethodTryEnterReadLock(Int32)

Intenta entrar en el bloqueo en modo de lectura, con un tiempo de espera entero opcional.

System_CAPS_pubmethodTryEnterReadLock(TimeSpan)

Intenta entrar en el bloqueo en modo de lectura, con tiempo de espera opcional.

System_CAPS_pubmethodTryEnterUpgradeableReadLock(Int32)

Intenta entrar en el bloqueo en modo de actualización, con tiempo de espera opcional.

System_CAPS_pubmethodTryEnterUpgradeableReadLock(TimeSpan)

Intenta entrar en el bloqueo en modo de actualización, con tiempo de espera opcional.

System_CAPS_pubmethodTryEnterWriteLock(Int32)

Intenta entrar en el bloqueo en modo de escritura, con tiempo de espera opcional.

System_CAPS_pubmethodTryEnterWriteLock(TimeSpan)

Intenta entrar en el bloqueo en modo de escritura, con tiempo de espera opcional.

Use ReaderWriterLockSlim para proteger un recurso que se leen varios subprocesos y escribir un subproceso a la vez. ReaderWriterLockSlim permite que varios subprocesos estén en modo de lectura, permite que un subproceso esté en modo de escritura con propiedad exclusiva del bloqueo y permite que un subproceso que tiene acceso de lectura a estar en modo de lectura actualizable desde el que puede actualizarse a modo de escritura sin tener que abandonar su acceso de lectura al recurso.

System_CAPS_noteNota

ReaderWriterLockSlim es similar a ReaderWriterLock, pero simplificó las reglas de recursividad y para actualizar y degradar el estado del bloqueo. ReaderWriterLockSlimevita muchos casos de interbloqueo potencial. Además, el rendimiento de ReaderWriterLockSlim es significativamente mejor que ReaderWriterLock. ReaderWriterLockSlim se recomienda para todo el desarrollo nuevo.

De forma predeterminada, las nuevas instancias de ReaderWriterLockSlim se crean con el LockRecursionPolicy.NoRecursion marca y no permiten la recursividad. Se recomienda esta directiva predeterminada para todo el desarrollo nuevo, porque la recursividad presenta complicaciones innecesarias y hace que el código sea más susceptible a los interbloqueos. Para simplificar la migración a partir de proyectos que utilizan Monitor o ReaderWriterLock, puede utilizar el LockRecursionPolicy.SupportsRecursion marca para crear instancias de ReaderWriterLockSlim que permite la recursividad.

Un subproceso puede entrar en el bloqueo en tres modos: modo de lectura actualizable, modo de escritura y modo de lectura. (En el resto de este tema, "modo de lectura actualizable" se denomina "modo de actualización" y la frase "escriba x modo" se utiliza con preferencia a las frases más largas "entrar en el bloqueo en x modo".)

Independientemente de la directiva de recursividad, sólo un subproceso puede estar en modo de escritura en cualquier momento. Cuando un subproceso está en modo de escritura, ningún otro subproceso puede entrar en el bloqueo en cualquier modo. Sólo un subproceso puede estar en modo de actualización en cualquier momento. Puede ser cualquier número de subprocesos en modo de lectura y puede haber un subproceso en modo de actualización mientras otros subprocesos están en modo de lectura.

System_CAPS_importantImportante

Este tipo implementa la IDisposable interfaz. Cuando haya terminado de utilizar el tipo, debería eliminar, directa o indirectamente. Para deshacerse del tipo directamente, llame a su Dispose método en un try/catch bloque. Para deshacerse de él indirectamente, usar una construcción de lenguaje como using (en C#) o Using (en Visual Basic). Para obtener más información, consulte la sección "Uso de un objeto que implementa IDisposable" en el IDisposable tema de la interfaz.

ReaderWriterLockSlim ha logrado la afinidad de subprocesos; es decir, cada Thread objeto debe realizar sus propias llamadas de método para entrar y salir de los modos de bloqueo. Ningún subproceso puede cambiar el modo de otro subproceso.

Si un ReaderWriterLockSlim no permite la recursividad, un subproceso que intenta entrar en el bloqueo se puede bloquear por varias razones:

  • Un subproceso que intenta entrar en modo de lectura se bloquea si hay subprocesos a la espera de entrar en modo de escritura o si hay un solo subproceso en modo de escritura.

    System_CAPS_noteNota

    El bloqueo de lectores de nuevo cuando se ponen en cola escritores es una directiva de equidad de bloqueo que favorece a escritores. La directiva de equidad actual equilibra la equidad para lectores y escritores, para mejorar el rendimiento en los escenarios más comunes. Las versiones futuras de la .NET Framework puede introducir nuevas directivas de equidad.

  • Un subproceso que intenta entrar en modo de actualización se bloquea si ya hay un subproceso en modo de actualización, si hay subprocesos a la espera de entrar en modo de escritura o si hay un solo subproceso en modo de escritura.

  • Un subproceso que intenta entrar en modo de escritura se bloquea si hay un subproceso en cualquiera de los tres modos.

Modo de actualización está pensado para los casos donde un subproceso normalmente lee el recurso protegido, pero podría necesitar escribir en él si se cumple alguna condición. Un subproceso que ha entrado en un ReaderWriterLockSlim en modo de actualización tiene acceso de lectura al recurso protegido y puede actualizarse al modo de escritura mediante una llamada a la EnterWriteLock o TryEnterWriteLock métodos. Porque puede haber solo un subproceso en modo de actualización en un momento, no se puede actualizar a modo de escritura interbloqueo cuando no se permite la recursividad, que es la directiva predeterminada.

System_CAPS_importantImportante

Independientemente de la directiva de recursividad, un subproceso que entró inicialmente lectura modo no está permitido actualizar al modo de actualización o en modo de escritura, porque ese modelo genera una probabilidad grande de interbloqueos. Por ejemplo, si dos subprocesos en modo de lectura intentan entrar en modo de escritura, se interbloqueará. Modo de actualización está diseñado para evitar estos interbloqueos.

Si hay otros subprocesos en modo de lectura, el subproceso que está actualizando se bloquea. Mientras el subproceso está bloqueado, se bloquean otros subprocesos que intenten entrar en modo de lectura. Cuando todos los subprocesos han salido del modo de lectura, el subproceso actualizable bloqueado entra en modo de escritura. Si hay otros subprocesos a la espera de entrar en modo de escritura, siguen bloqueados, porque el subproceso que está en modo de actualización evita que obtengan acceso exclusivo al recurso.

Cuando el subproceso en modo de actualización sale del modo de escritura, otros subprocesos que están a la espera de entrar en modo de lectura pueden hacerlo, a menos que haya subprocesos esperando para entrar en modo de escritura. El subproceso en modo de actualización puede actualizar y degradarse indefinidamente, siempre y cuando sea el único subproceso que escribe en el recurso protegido.

System_CAPS_importantImportante

Si permite que varios subprocesos entren en modo de escritura o en modo de actualización, no debe permitir que un subproceso monopolice el modo de actualización. De lo contrario, los subprocesos que intenten entrar escriben modo directamente se bloquearán indefinidamente y, mientras estén bloqueados, otros subprocesos no podrá entrar en modo de lectura.

Un subproceso en modo de actualización puede degradarse a modo de lectura llamando primero la EnterReadLock método y, a continuación, llamar a la ExitUpgradeableReadLock (método). Este modelo de degradación se permite para todas las directivas de recursividad de bloqueo, incluso NoRecursion.

Después de degradar el modo de lectura, un subproceso no puede volver a escribir en modo de actualización hasta que se ha salido del modo de lectura.

Puede crear un ReaderWriterLockSlim que admite la entrada de bloqueo recursiva utilizando la ReaderWriterLockSlim(LockRecursionPolicy) constructor que especifica la directiva de bloqueo y la especificación de LockRecursionPolicy.SupportsRecursion.

System_CAPS_noteNota

El uso de la recursividad no se recomienda para nuevo desarrollo, porque presenta complicaciones innecesarias y hace que el código sea más susceptible a los interbloqueos.

Para un ReaderWriterLockSlim que permite la recursividad, se puede decir lo siguiente acerca de los modos en que un subproceso puede entrar:

  • Un subproceso en modo de lectura puede entrar de forma recursiva de modo de lectura, pero no puede entrar en modo de escritura o en modo de actualización. Si intenta hacerlo, un LockRecursionException se produce. Escribir lee modo y, a continuación, entrar en modo de escritura o en modo de actualización es un modelo con muchas probabilidades de interbloqueos, por lo que no está permitido. Como se explicó anteriormente, en modo de actualización se proporciona para los casos donde sea necesario actualizar un bloqueo.

  • Un subproceso en modo de actualización puede entrar en modo de escritura y/o modo de lectura y puede especificar cualquiera de los tres modos de forma recursiva. Sin embargo, se intenta escribir escribir bloques de modo si hay otros subprocesos en modo de lectura.

  • Un subproceso en modo de escritura puede entrar en modo de lectura y/o en modo de actualización y puede especificar cualquiera de los tres modos de forma recursiva.

  • Un subproceso que no ha entrado en el bloqueo puede entrar en cualquier modo. Se puede bloquear este intento por las mismas razones que un intento de entrar en un bloqueo no recursivo.

Un subproceso puede salir de los modos que ha entrado en cualquier orden, siempre y cuando sale de cada modo exactamente como tantas veces como entró en ese modo. Si un subproceso intenta salir de un modo demasiadas veces o salir de un modo que no ha entrado un SynchronizationLockException se produce.

Puede que le resulte útil considerar el bloqueo en términos de sus Estados. Un ReaderWriterLockSlim puede estar en uno de cuatro estados: no se especifica, leer, actualizar y escribir.

  • No entrado: en este estado, no hay ningún subproceso ha entrado en el bloqueo (o todos los subprocesos han salido del bloqueo).

  • Lectura: En este estado, uno o más subprocesos han entrado en el bloqueo para el acceso de lectura al recurso protegido.

    System_CAPS_noteNota

    Un subproceso puede entrar en el bloqueo en modo de lectura mediante el uso de la EnterReadLock o TryEnterReadLock métodos, o degradar de modo de actualización.

  • Actualización: En este estado, un subproceso ha entrado en el bloqueo para el acceso de lectura con la opción de actualización para acceso de escritura (es decir, en modo de actualización), y cero o más subprocesos han entrado en el bloqueo para el acceso de lectura. No más de un subproceso a la vez puede entrar en el bloqueo con la opción de actualización; se bloquean otros subprocesos que intenten entrar en modo de actualización.

  • Escritura: En este estado, un subproceso ha entrado en el bloqueo para el acceso de escritura al recurso protegido. Ese subproceso tiene posesión exclusiva del bloqueo. Se bloquea cualquier otro subproceso que intenta entrar en el bloqueo por cualquier motivo.

La tabla siguiente describen las transiciones entre Estados de bloqueo, para los bloqueos que no permiten la recursividad, cuando un subproceso t toma la acción descrita en la columna izquierda. Cuando se realiza la acción t no tiene ningún modo. (El caso especial donde t está en modo de actualización se describe en las notas al pie de tabla.) La fila superior describe el estado inicial del bloqueo. Las celdas describen lo que sucede al subproceso y mostrar los cambios en el estado de bloqueo entre paréntesis.

No se ha introducido (N)

Read (R)

Actualización (U)

Escritura (W)

t entra en modo de lectura

t Introduce (R).

t se bloquea si hay subprocesos en espera para el modo de escritura; de lo contrario, t entra.

t se bloquea si hay subprocesos en espera para el modo de escritura; de lo contrario, t entra.1

t se bloquea.

t entra en modo de actualización

t entra en (A).

t se bloquea si hay subprocesos en espera para modo de escritura o modo de actualización; de lo contrario, t entra en (A).

t se bloquea.

t se bloquea.

t entra en modo de escritura

t Introduce (W).

t se bloquea.

t se bloquea.2

t se bloquea.

1 si t inicia out en modo de actualización, entra en modo de lectura. Esta acción nunca se bloquea. No cambia el estado de bloqueo. (El subproceso, a continuación, puede completar una degradación a modo de lectura saliendo del modo de actualización.)

2 si t empieza en modo de actualización, se bloquea si hay subprocesos en modo de lectura. De lo contrario, se actualiza a modo de escritura. Los cambios de estado de bloqueo de escritura (W). Si t se bloquea porque hay subprocesos en modo de lectura, entra en modo de escritura en cuanto el último subproceso sale del modo de lectura, incluso si hay subprocesos esperando para entrar en modo de escritura.

Cuando se produce un cambio de estado porque un subproceso sale del bloqueo, el siguiente subproceso que debe activarse se selecciona como sigue:

  • En primer lugar, un subproceso que espera en modo de escritura y ya está en modo de actualización (puede haber a lo sumo un subproceso de este tipo).

  • Si no es posible, un subproceso que está esperando a modo de escritura.

  • Si no es posible, un subproceso que espera en modo de actualización.

  • Si no es posible, todos los subprocesos que están esperando a modo de lectura.

El estado posterior del bloqueo siempre es escritura (W) en los dos primeros casos y actualización (A) en el tercer caso, independientemente del estado del bloqueo cuando el subproceso que sale activa el cambio de estado. En este último caso, el estado del bloqueo es actualización (A) si hay un subproceso en modo de actualización después del cambio de estado y Read (R) de lo contrario, con independencia del estado anterior.

En el ejemplo siguiente se muestra una memoria caché sincronizada simple que contiene cadenas con claves de enteros. Una instancia de ReaderWriterLockSlim se utiliza para sincronizar el acceso a la Dictionary<TKey, TValue> que actúa como caché interna.

El ejemplo incluye métodos sencillos para agregar a la caché, eliminar de la caché y leer desde la memoria caché. Para mostrar los tiempos de espera, el ejemplo incluye un método que agrega a la memoria caché solo si puede hacerlo dentro de un tiempo de espera especificado.

Para demostrar el modo de actualización, el ejemplo incluye un método que recupera el valor asociado a una clave y lo compara con un nuevo valor. Si el valor se ha modificado, el método devuelve un estado que indica ningún cambio. Se encuentra ningún valor para la clave, se inserta el par clave/valor. Si el valor ha cambiado, se actualiza. Modo de actualización permite al subproceso actualizarse del acceso de lectura a acceso de escritura según sea necesario, sin riesgo de interbloqueos.

El ejemplo incluye una enumeración anidada que especifica los valores devueltos del método que se muestra en modo de actualización.

En el ejemplo se utiliza el constructor predeterminado para crear el bloqueo, por lo que no se permite la recursividad. Programación del ReaderWriterLockSlim es más sencillo y menos propenso a errores cuando el bloqueo no permite la recursividad.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
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();
    }
}

El siguiente código a continuación, utiliza el SynchronizedCache objeto para almacenar un diccionario de nombres vegetales. Crea tres tareas. El primero escribe los nombres de verduras almacenados en una matriz a un SynchronizedCache instancia. La tarea de segunda y tercer mostrar los nombres de las verduras, el primero en orden ascendente (de bajo índice alto), el segundo en orden descendente. La tarea final busca la cadena "cucumber" y, si lo encuentra, llama el EnterUpgradeableReadLock método sustituir la cadena "bean verde".

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
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

Plataforma universal de Windows
Disponible desde 8
.NET Framework
Disponible desde 3.5
Biblioteca de clases portable
Se admite en: plataformas portátiles de .NET
Windows Phone Silverlight
Disponible desde 8.0
Windows Phone
Disponible desde 8.1

Este tipo es seguro para la ejecución de subprocesos.

Volver al principio
Mostrar: