utilice una solucion quite quitar protegido proteccion otro memoria escritura duro disco desde definitiva contra como delphi synchronization delphi-2009

delphi - una - usb protegido contra escritura solucion



Comportamiento de TMultiReadExclusiveWriteSynchronizer al promover el bloqueo de lectura para el bloqueo de escritura (4)

Gracias a las respuestas de Uwe Raabe y Tihauan:

TMultiReadExclusiveWriteSynchronizer funciona bien con tales estructuras de bloqueo anidadas. EndWrite no libera el bloqueo de lectura, por lo que es posible promotoar un bloqueo de lectura a un bloqueo de escritura durante un cierto período de tiempo y luego volver al bloqueo de lectura sin que otros autores interfieran.

¿Cómo puedo lograr una estructura de sincronización como esa?

Lock.BeginRead try if Changed then begin Lock.BeginWrite; try Update; finally Lock.EndWrite; end; // ... do some other stuff ... end; finally Lock.EndRead; end;

sin perder el bloqueo de lectura después de EndWrite, para que no se puedan ejecutar otros escritores mientras se ejecuta este bloque de código.

¿Cómo se comporta TMuliReadExclusiveWriteSynchronizer de Delphi 2009 en este caso?


No tengo Delphi 2009, pero espero que no haya cambios en la forma en que funciona TMultiReadExclusiveWriteSynchronizer. Creo que es la estructura correcta para usar para su escenario con un comentario: el "BeginWrite" es una función que devuelve un booleano. Asegúrese de verificar su resultado antes de realizar las operaciones de escritura.

Además, en Delphi 2006, la clase TMultiReadExclusiveWriteSynchronizer tiene muchos comentarios de desarrolladores y también algunos códigos de depuración. Asegúrese de echar un vistazo a la implementación antes de usarla.

Vea también: Trabajar con TMultiReadExclusiveWriteSynchronizer en EDN


Parece que hay dos criterios envueltos en esta pregunta:

  • "sin perder el bloqueo de lectura después de EndWrite"
  • "no se pueden ejecutar otros escritores mientras se ejecuta este bloque de código"

No abordaré el primer punto más lejos ya que otros ya lo han hecho. Sin embargo, el segundo punto es muy delicado y necesita explicación.

Antes que nada, permítanme decir que me refiero a Delphi 2007. No tengo acceso a 2009. Sin embargo, es poco probable que el comportamiento que estoy describiendo haya cambiado.

El código que muestra hace posible que otros escritores cambien el valor durante el bloque de código. Cuando el bloqueo de lectura se promueve a un bloqueo de escritura, el bloqueo de lectura se pierde temporalmente. Hay un instante en el que su hilo no tiene ni un bloqueo de lectura ni de escritura. Esto es por diseño, ya que de otro modo el punto muerto sería casi seguro. Si el hilo que está promoviendo un bloqueo de lectura en un bloqueo de escritura mantiene el bloqueo de lectura mientras lo hace, la siguiente situación podría ocurrir fácilmente:

  1. (thread 1) obtener bloqueo de lectura
  2. (Tema 2) Obtener bloqueo de lectura (de acuerdo, se comparte el bloqueo de lectura)
  3. (subproceso 1) obtener bloqueo de escritura (bloques; el subproceso 2 tiene un bloqueo de lectura)
  4. (subproceso 2) obtener bloqueo de escritura (bloques; el subproceso 1 tiene un bloqueo de lectura, ahora está en punto muerto)

Para evitar esto, TMuliReadExclusiveWriteSynchronizer libera el bloqueo de lectura durante algún "instante" antes de obtener el bloqueo de escritura.

(Nota al margen : el artículo Trabajando con TMultiReadExclusiveWriteSynchronizer en EDN, en la sección "Lock it up Chris, estoy a punto de ..." parece sugerir incorrectamente que el escenario que acabo de mencionar realmente se estancaría. Esto podría haberse escrito sobre una versión anterior de Delphi o podría estar equivocada. O podría estar malinterpretando lo que está reclamando. Sin embargo, observe algunos de los comentarios sobre el artículo).

Entonces, no asumiendo nada más sobre el contexto, el código que ha mostrado es casi seguro incorrecto. Verificando un valor mientras tiene un bloqueo de lectura, promocionarlo a un bloqueo de escritura y asumir que el valor no ha cambiado es un error. Esta es una captura muy sutil con TMuliReadExclusiveWriteSynchronizer.

Aquí hay algunas partes seleccionadas de los comentarios en el código de la biblioteca Delphi:

Otros subprocesos tienen la oportunidad de modificar el recurso protegido cuando llama a BeginWrite antes de que se le otorgue el bloqueo de escritura, incluso si ya tiene abierto un bloqueo de lectura. La mejor política es no retener ninguna información sobre el recurso protegido (como el recuento o el tamaño) a través de un bloqueo de escritura. Siempre vuelva a adquirir muestras del recurso protegido después de adquirir o liberar un bloqueo de escritura. El resultado de la función de BeginWrite indica si otro hilo obtuvo el bloqueo de escritura mientras el hilo actual estaba esperando el bloqueo de escritura. El valor de retorno de True significa que el bloqueo de escritura fue adquirido sin modificaciones intermedias por otros hilos. El valor de retorno de False significa que otro hilo obtuvo el bloqueo de escritura mientras esperaba, por lo que el recurso protegido por el objeto MREWS debe considerarse modificado. Cualquier muestra del recurso protegido debe descartarse. En general, es mejor simplemente volver a adquirir muestras del recurso protegido después de obtener un bloqueo de escritura. El resultado booleano de BeginWrite y la propiedad RevisionLevel ayudan a los casos en los que readquirir las muestras es computacionalmente costoso o requiere mucho tiempo.

Aquí hay un código para probar. Cree un TMultiReadExclusiveWriteSynchronizer global llamado Lock. Crea dos Booleanos globales: Malo y GlobalB. Luego, inicie una instancia de cada uno de estos subprocesos y monitoree el valor de Bad desde su cadena principal de programa.

type TToggleThread = class(TThread) protected procedure Execute; override; end; TTestThread = class(TThread) protected procedure Execute; override; end; { TToggleThread } procedure TToggleThread.Execute; begin while not Terminated do begin Lock.BeginWrite; try GlobalB := not GlobalB; finally Lock.EndWrite; end; end; end; { TTestThread } procedure TTestThread.Execute; begin while not Terminated do begin Lock.BeginRead; try if GlobalB then begin Lock.BeginWrite; try if not GlobalB then begin Bad := True; Break; end; finally Lock.EndWrite; end; end; finally Lock.EndRead; end; end; end;

Aunque no es determinista, probablemente verá muy rápidamente (menos de 1 segundo) que el valor Bad se establece en True. Así que, básicamente, ves que el valor de GlobalB es Verdadero y luego cuando lo revisas por segunda vez es Falso, aunque ambos controles ocurrieron entre un par BeginRead / EndRead (y el motivo es porque también había un par BeginWrite / EndWrite dentro) .

Mi consejo personal: nunca promocione un bloqueo de lectura en un bloqueo de escritura. Es demasiado fácil equivocarse. En cualquier caso, nunca promocionará realmente un bloqueo de lectura en un bloqueo de escritura (porque pierde temporalmente el bloqueo de lectura), por lo que puede hacerlo explícito en el código simplemente llamando a EndRead antes de BeginWrite. Y sí, eso significa que tendría que verificar la condición nuevamente dentro de BeginWrite. Entonces, en el caso del código que mostraste originalmente, ni siquiera me molestaría con un bloqueo de lectura. Simplemente comience con BeginWrite porque puede decidir escribir.


Primero: su código de EndWrite reside en TSimpleRWSync, que es una implementación liviana de IReadWriteSync, mientras que TMultiReadExclusiveWriteSynchronizer es mucho más sofisticado.

Segundo: la llamada a LeaveCriticalSection (FLock) en EndWrite no libera el bloqueo si todavía hay algunas llamadas abiertas a EnterCriticalSection (FLock) (como la de BeginRead).

Esto significa que su ejemplo de código es bastante válido y debería funcionar como se esperaba si está utilizando una instancia de TSimpleRWSync o una instancia de TMultiReadExclusiveWriteSynchronizer.