Articulo de referencia

Bloqueo lector-escritor

En informática , un bloqueo de lectura-escritura ( bloqueo de escritura única , [ 1 ] bloqueo de lectura múltiple , [ 2 ] bloqueo de inserción , [ 3 ] o bloqueo MRSW ) es una pr...

En informática , un bloqueo de lectura-escritura ( bloqueo de escritura única , [ 1 ] bloqueo de lectura múltiple , [ 2 ] bloqueo de inserción , [ 3 ] o bloqueo MRSW ) es una primitiva de sincronización que resuelve uno de los problemas de lectura-escritura . Un bloqueo RW permite el acceso concurrente para operaciones de solo lectura, mientras que las operaciones de escritura requieren acceso exclusivo. Esto significa que varios hilos pueden leer los datos en paralelo, pero se necesita un bloqueo exclusivo para escribir o modificar datos. Cuando un escritor está escribiendo los datos, todos los demás escritores y lectores se bloquearán hasta que el escritor termine de escribir. Un uso común podría ser controlar el acceso a una estructura de datos en memoria que no se puede actualizar atómicamente y es inválida (y no debe ser leída por otro hilo) hasta que la actualización se complete.

Los bloqueos de lectura-escritura generalmente se construyen sobre mutexes y variables de condición , o sobre semáforos .

Cerradura RW actualizable

Algunos bloqueos RW permiten actualizar atómicamente el bloqueo de modo de lectura a modo de escritura, así como degradarlo de modo de escritura a modo de lectura. [ 4 ] La actualización de un bloqueo de modo de lectura a modo de escritura es propensa a interbloqueos, ya que cuando dos hilos que poseen bloqueos de lectura intentan actualizarse a bloqueos de escritura, se crea un interbloqueo que solo puede romperse si uno de los hilos libera su bloqueo de lectura. El interbloqueo puede evitarse permitiendo que solo un hilo adquiera el bloqueo en "modo de lectura con la intención de actualizarse a escritura" mientras no haya hilos en modo de escritura y posiblemente no haya hilos en modo de lectura.

Políticas prioritarias

Los bloqueos RW pueden diseñarse con distintas políticas de prioridad para el acceso de lectores y escritores. El bloqueo puede diseñarse para dar siempre prioridad a los lectores ( preferencia de lectura ), para dar siempre prioridad a los escritores ( preferencia de escritura ) o no especificar ninguna prioridad. Estas políticas conllevan diferentes ventajas y desventajas en cuanto a la concurrencia y la inanición .

  • Los bloqueos RW con preferencia de lectura permiten la máxima concurrencia, pero pueden provocar inanición de escritura si la contención es alta. Esto se debe a que los hilos de escritura no podrán adquirir el bloqueo mientras al menos un hilo de lectura lo mantenga. Dado que varios hilos de lectura pueden mantener el bloqueo simultáneamente, esto significa que un hilo de escritura puede seguir esperando el bloqueo mientras nuevos hilos de lectura pueden adquirirlo, incluso hasta el punto de que el escritor puede seguir esperando después de que todos los lectores que lo mantenían cuando intentó adquirirlo por primera vez lo hayan liberado. La prioridad para los lectores puede ser débil , como se acaba de describir, o fuerte , lo que significa que cada vez que un escritor libera el bloqueo, cualquier lector bloqueador lo adquiere a continuación. [ 5 ] : 76
  • Los bloqueos RW con preferencia de escritura evitan el problema de la inanición de escritores al impedir que nuevos lectores adquieran el bloqueo si hay un escritor en cola esperando; el escritor adquirirá el bloqueo tan pronto como todos los lectores que ya lo poseían hayan finalizado. [ 6 ] La desventaja es que los bloqueos con preferencia de escritura permiten una menor concurrencia en presencia de hilos escritores, en comparación con los bloqueos RW con preferencia de lectura. Además, el rendimiento del bloqueo es menor porque cada operación, ya sea tomar o liberar el bloqueo para lectura o escritura, es más compleja, requiriendo internamente tomar y liberar dos mutex en lugar de uno. Esta variación también se conoce a veces como bloqueo lector-escritor con "sesgo de escritura". [ 7 ]
  • Los bloqueos RW de prioridad no especificada no ofrecen ninguna garantía en cuanto al acceso de lectura frente al de escritura. En algunas situaciones, la prioridad no especificada puede ser preferible si permite una implementación más eficiente.

Implementación

Existen diversas estrategias de implementación para los bloqueos de lectores y escritores, que los reducen a primitivas de sincronización que se presuponen preexistentes.

Utilizando dos mutex

Raynal demuestra cómo implementar un bloqueo de lectura/escritura utilizando dos mutex y un contador entero. El contador, b , registra el número de lectores que bloquean el proceso. Un mutex, r , protege a b y solo lo utilizan los lectores; el otro, g (de "global"), garantiza la exclusión mutua de los escritores. Esto requiere que un mutex adquirido por un hilo pueda ser liberado por otro. A continuación se muestra el pseudocódigo para las operaciones:

Inicializar

Establece b en 0. r está desbloqueado. g está desbloqueado.

Comienza a leer

Bloquear r . Incremento b . Si b = 1 , bloquea g . Desbloquear r .

Fin de la lectura

Bloquear r . Disminución b . Si b = 0 , desbloquea g . Desbloquear r .

Comienza a escribir

Bloquear g .

Fin de la escritura

Desbloquear g .

Esta implementación da preferencia a la lectura. [ 5 ] : 76

Utilizando una variable de condición y un mutex.

Alternativamente, un bloqueo RW puede implementarse en términos de una variable de condición , cond , un bloqueo ordinario (mutex), g , y varios contadores y banderas que describen los hilos que están actualmente activos o en espera. [ 8 ] [ 9 ] [ 10 ] Para un bloqueo RW con preferencia de escritura, se pueden usar dos contadores enteros y una bandera booleana:

num_readers_active : el número de lectores que han adquirido el bloqueo (entero)
num_writers_waiting : el número de escritores que esperan acceso (entero)
writer_active : indica si un escritor ha adquirido el bloqueo (booleano).

Inicialmente , num_readers_active y num_writers_waiting son cero y writer_active es falso.

Las operaciones de bloqueo y liberación se pueden implementar como

Comienza a leer

Bloquear g Mientras num_writers_waiting > 0 o writer_active : esperar cond , g [ a ] Incrementar num_readers_active Desbloquear g .

Fin de la lectura

Bloquear g Decrementar num_readers_active Si num_readers_active = 0 : Notificar condición (difusión) Desbloquear g .

Comienza a escribir

Bloquear g Incrementar num_writers_waiting Mientras num_readers_active > 0 o writer_active sea verdadero : esperar cond , g Decrementar num_writers_waiting Establecer writer_active en verdadero Desbloquear g .

Fin de la escritura

Bloquear g Establecer writer_active en falso Notificar cond (difusión) Desbloquear g .

Compatibilidad con lenguajes de programación

Ejemplo en Rust

usar std :: sync :: RwLock ;let lock = RwLock :: new ( 5 );// Se pueden mantener varios bloqueos de lectura a la vez { let r1 = lock . read (). unwrap (); let r2 = lock . read (). unwrap (); assert_eq! ( * r1 , 5 ); assert_eq! ( * r2 , 5 ); } // Los bloqueos de lectura se liberan en este punto// Solo se puede mantener un bloqueo de escritura, sin embargo { let mut w = lock . write (). unwrap (); * w += 1 ; assert_eq! ( * w , 6 ); } // El bloqueo de escritura se libera aquí

Alternativas

El algoritmo de lectura-copia-actualización (RCU) es una solución al problema de lectores-escritores. RCU no requiere esperas para los lectores. El kernel de Linux implementa una solución especial para algunos escritores llamada seqlock .

Véase también

Notas

  1. Esta es la operación estándar de "espera" en variables de condición, que, entre otras acciones, libera el mutex g .

Referencias

  1. Hamilton, Doug (21 de abril de 1995). "¿Sugerencias para el bloqueo de múltiples lectores/un solo escritor?" . Grupo de noticias : comp.os.ms-windows.nt.misc . Usenet: hamilton.798430053@BIX.com . Recuperado el 8 de octubre de 2010 .  
  2. "Libertad práctica de cerraduras" por Keir Fraser, 2004
  3. "Bloqueos de empuje: ¿qué son?" . Blog de Ntdebugging . Blogs de MSDN. 2 de septiembre de 2009 . Consultado el 11 de mayo de 2017 .
  4. "Sincronización § Concepto de actualización bloqueable – EXTENSIÓN" . Bibliotecas Boost C++ .
  5. 1 2 Raynal, Michel (2012). Programación concurrente: algoritmos, principios y fundamentos . Springer.
  6. Stevens, W. Richard ; Rago, Stephen A. (2013). Programación avanzada en el entorno UNIX . Addison-Wesley. pág. 409. 
  7. 1 2java.util.concurrent.locks.ReentrantReadWriteLock La implementación del bloqueo de lectores-escritores de Java ofrece un modo "justo".
  8. Herlihy, Maurice; Shavit, Nir (2012). El arte de la programación multiprocesador . Elsevier. págs. 184–185 . 
  9. Nichols, Bradford; Buttlar, Dick; Farrell, Jacqueline (1996). PThreads Programming: A POSIX Standard for Better Multiprocessing . O'Reilly. pp. 84–89 . ISBN  9781565921153.
  10. Butenhof, David R. (1997). Programación con hilos POSIX . Addison-Wesley. págs. 253–266 . 
  11. "Especificaciones básicas de The Open Group, número 6, IEEE Std 1003.1, edición de 2004: pthread_rwlock_destroy" . IEEE y The Open Group . Consultado el 14 de mayo de 2011 .
  12. java.util.concurrent.locks.ReadWriteLock
  13. "Clase ReaderWriteLockSlim (System.Threading)" . Microsoft Corporation . Consultado el 14 de mayo de 2011 .
  14. "Nuevo documento adoptado: N3659, Bloqueo compartido en C++—Howard Hinnant, Detlef Vollmann, Hans Boehm" . Fundación del estándar C++.
  15. Anthony Williams. "Sincronización – Boost 1.52.0" . Consultado el 31 de enero de 2012 .
  16. Alessandrini, Victor (2015). Programación de aplicaciones con memoria compartida: conceptos y estrategias en la programación de aplicaciones multinúcleo . Morgan Kaufmann.
  17. "El lenguaje de programación Go: sincronización de paquetes" . Consultado el 30 de mayo de 2015 .
  18. "Sincronización lector-escritor para sistemas multiprocesador en tiempo real con memoria compartida" (PDF) .
  19. "std::sync::RwLock – Rust" . Consultado el 26 de octubre de 2019 .
  20. "Bloqueo de lectores/escritores para Twisted" . GitHub . Consultado el 28 de septiembre de 2016 .
  21. "Primitivas de sincronización en el kernel de Linux: semáforos de lectura/escritura" . Linux Insides . Consultado el 8 de junio de 2023 .