2011-11-23 18 views
5

Estoy intentando usar la clase ReaderWriterLockSlim para administrar una lista.ReaderWriterLockSlim El bloqueo se lee hasta que se completen todas las escrituras en cola

Hay muchas lecturas en esta lista y pocas escrituras, mis lecturas son rápidas mientras que mis escrituras son lentas.

Tengo un arnés de prueba simple escrito para comprobar cómo funciona el bloqueo.

Si se produce la siguiente situación

Thread 1 - Start Write 
Thread 2 - Start Read 
Thread 3 - Start Write 

A continuación, el resultado es el siguiente

Thread 1 starts its write and locks the list. 
Thread 2 adds itself to the read queue. 
Thread 3 adds itself to the write queue. 
Thread 1 finishes writing and releases the lock 
Thread 3 aquires the lock and starts its write 
Thread 3 finishes writing and releases the lock 
Thread 2 performs its read 

¿Hay alguna forma de cambiar el comportamiento de la cerradura de manera que todas las solicitudes de lectura que fueron puestas en la cola antes de una Se permite completar el bloqueo de escritura antes de que se otorguen los bloqueos de escritura.

EDIT: El código que demuestra el problema que tengo es inferior a

public partial class SimpleLock : System.Web.UI.Page 
{ 
    public static ReaderWriterLockSlim threadLock = new ReaderWriterLockSlim(); 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     List<String> outputList = new List<String>(); 

     Thread thread1 = new Thread(
      delegate(object output) 
      { 
       ((List<String>)output).Add("Write 1 Enter"); 
       threadLock.EnterWriteLock(); 
       ((List<String>)output).Add("Write 1 Begin"); 
       Thread.Sleep(100); 
       ((List<String>)output).Add("Write 1 End"); 
       threadLock.ExitWriteLock(); 
       ((List<String>)output).Add("Write 1 Exit"); 
      } 
     ); 
     thread1.Start(outputList); 

     Thread.Sleep(10); 

     Thread thread2 = new Thread(
      delegate(object output) 
      { 
       ((List<String>)output).Add("Read 2 Enter"); 
       threadLock.EnterReadLock(); 
       ((List<String>)output).Add("Read 2 Begin"); 
       Thread.Sleep(100); 
       ((List<String>)output).Add("Read 2 End"); 
       threadLock.ExitReadLock(); 
       ((List<String>)output).Add("Read 2 Exit"); 
      } 
     ); 
     thread2.Start(outputList); 

     Thread.Sleep(10); 

     Thread thread3 = new Thread(
      delegate(object output) 
      { 
       ((List<String>)output).Add("Write 3 Enter"); 
       threadLock.EnterWriteLock(); 
       ((List<String>)output).Add("Write 3 Begin"); 
       Thread.Sleep(100); 
       ((List<String>)output).Add("Write 3 End"); 
       threadLock.ExitWriteLock(); 
       ((List<String>)output).Add("Write 3 Exit"); 
      } 
     ); 
     thread3.Start(outputList); 

     thread1.Join(); 
     thread2.Join(); 
     thread3.Join(); 

     Response.Write(String.Join("<br />", outputList.ToArray())); 
    } 
} 
+0

La respuesta directa a su pregunta, "¿Hay alguna forma de cambiar el comportamiento de la cerradura," la respuesta es no. La documentación de [ReaderWriterLockSlim] (http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx) dice: "Un hilo que intenta ingresar a los bloques de modo de lectura si hay hilos esperando ingresar escribir modo o si hay un solo hilo en modo de escritura ". Actualmente no hay una manera de cambiar ese comportamiento. –

Respuesta

3

¿Hay alguna forma de cambiar el comportamiento de la cerradura de modo que cualquier las peticiones de lectura que fueron puestas en la cola antes de que un bloqueo de escritura son permitido a completar antes de que se otorguen los bloqueos de escritura?

¿Qué tal evitar el uso de cerraduras casi por completo? Durante una escritura, puede adquirir un bloqueo, copiar la estructura de datos original, modificar la copia y luego publicar la nueva estructura de datos intercambiando la referencia anterior con la nueva referencia. Como nunca modifica la estructura de datos después de que ha sido "publicada", entonces no necesitaría bloquear las lecturas.

Así es como funciona:

public class Example 
{ 
    private object writelock = new object(); 
    private volatile List<string> data = new List<string>(); 

    public void Write(string item) 
    { 
    lock (writelock) 
    { 
     var copy = new List<string>(data); // Create the copy. 
     copy.Add(item); // Modify the data structure. 
     data = copy; // Publish the modified data structure. 
    } 
    } 

    public string Read(int index) 
    { 
    return data[index]; 
    } 
} 

El truco aquí estamos explotando es la inmutabilidad de lo que es referencia por la variable data. Lo único que debemos hacer es marcar la variable como volatile.

Tenga en cuenta que este truco solo funciona si las escrituras son suficientemente infrecuentes y la estructura de datos es lo suficientemente pequeña como para mantener la operación de copia barata. No es el ser todo el extremo solución. No es ideal para todos los escenarios, pero puede funcionar para usted.

+0

Este es el método que hemos seguido. En realidad tiene una gran sobrecarga ya que el tamaño de la "lista" es grande. Lleva mucho tiempo hacer una copia. Sin embargo, por el momento parece estar resistiendo. –

+1

Para agregar a esto, en su ejemplo, durante la escritura, crea una tercera versión de la lista con la que luego reemplaza la instantánea. En realidad, puede simplemente mantener la lista "maestra" de la cual también se realizan todas las lecturas. Luego, una escritura puede "bloquear" el maestro para que no se produzcan más escrituras. Su primer paso es hacer una copia del maestro. Luego actualiza la copia según sea necesario, luego reemplaza "maestro" con la copia. –

+0

@RobinDay: Sí, excelente captura! No había pensado en eso.Eso reduce el consumo de memoria bastante. Hice algunos cambios significativos en mi respuesta para reflejar su idea. –

Cuestiones relacionadas