2012-03-08 17 views
8

Me pregunto si el siguiente (pseudo) código es seguro de usar. Sé acerca de la bandera Terminated, pero necesito establecer algún tipo de indicador de cancelación en la operación de búsqueda recursiva desde el hilo principal y mantener el thread worker en funcionamiento. Comprobaré allí también la propiedad Terminated, lo que falta en este pseudo código.¿Es seguro establecer el valor booleano en el hilo desde otro?

type 
    TMyThread = class(TThread) 
    private 
    FCancel: Boolean; 
    procedure RecursiveSearch(const ItemID: Integer); 
    protected 
    procedure Execute; override; 
    public 
    procedure Cancel; 
end; 

procedure TMyThread.Cancel; 
begin 
    FCancel := True; 
end; 

procedure TMyThread.Execute; 
begin 
    RecursiveSearch(0); 
end; 

procedure TMyThread.RecursiveSearch(const ItemID: Integer); 
begin 
    if not FCancel then 
    RecursiveSearch(ItemID); 
end; 

procedure TMainForm.ButtonCancelClick(Sender: TObject); 
begin 
    MyThread.Cancel; 
end; 

¿Es seguro para establecer la propiedad booleana FCancel en el interior de la rosca de esta manera? ¿No colisionaría esto con la lectura de esta bandera en el procedimiento de RecursiveSearch mientras se presiona el botón en la forma principal (hilo principal)? O tendré que agregar, p. sección crítica para leer y escribir este valor?

Muchas gracias

+1

Si mal no recuerdo, una asignación entera es atómica y, por lo tanto, segura para hilos. – iamjoosy

+3

Parece que duplicar la propiedad 'Terminated' ... debería ser seguro. – jpfollenius

+0

Gracias a todos; El comentario de Smasher sobre la duplicación de propiedades terminadas es la mejor explicación. –

Respuesta

16

Es perfectamente seguro hacer esto. El hilo de lectura siempre leerá verdadero o falso. No habrá rasgado porque un Boolean es solo un byte. De hecho, lo mismo es cierto para un valor alineado de 32 bits en un proceso de 32 bits, es decir, Integer.

Esto es lo que se conoce como raza benigna. Hay una condición de carrera en la variable booleana dado que un hilo se lee mientras otro hilo escribe, sin sincronización. Pero la lógica de este programa no se ve afectada adversamente por la raza. En escenarios más complejos, una carrera así podría ser dañina y luego se necesitaría sincronización.

3

Sí, es seguro, necesita usar Secciones críticas solo cuando está leyendo o escribiendo desde/a otro hilo, dentro del mismo hilo es seguro.

BTW. la forma en que tiene RecursiveSearch método definido, si (FCancel = False) entonces obtendrá un desbordamiento de pila (:

+0

Bastante elegante :-) Pero sobre la sección crítica, de hecho estoy escribiendo el valor del hilo principal (desde el que ejecuto MyThread.Cancel) en el valor que pertenece al hilo de trabajo, eso es lo que me preocupa. Pero el mejor ejemplo, como dijo Smasher, es la propiedad Terminated. ¡Gracias de cualquier manera! –

+1

Esto no es realmente correcto. Puede ser perfectamente seguro escribir en un entero alineado de un hilo y leerlo desde un hilo diferente. Un buen ejemplo sería un contador de progreso que aumenta de 0 a 100. Mientras que los hilos de escritura y lectura están compitiendo con la variable, esta carrera sería benigna, al menos para este ejemplo. –

+0

@David gracias por corregir, siempre he usado secciones críticas, no sabía que podría ser seguro en algunas situaciones sin él. – ComputerSaysNo

7

Escribir en un campo booleano de diferentes hilos de rosca es segura - es decir, la operación de escritura es atómica. Ningún observador del campo verá jamás un "valor parcial" a medida que se escribe el valor en el campo. Con tipos de datos más grandes, las escrituras parciales son una posibilidad real porque se requieren varias instrucciones de la CPU para escribir el valor en el campo

Por lo tanto, la escritura real de booleano no es un problema de seguridad de subprocesos, sin embargo, la forma en que los observadores usan ese campo booleano puede ser un problema de seguridad de subprocesos. En su ejemplo, el único observador visible es la función RecursiveSearch y su uso de la F Cancelar el valor es bastante simple e inofensivo. El observador del estado FCancel no cambia el estado de FCancel, por lo que esta es una dependencia de tipo productor/consumidor lineal/acíclica.

Si, en cambio, el código utilizaba el campo booleano para determinar si se debía realizar una operación única, las lecturas y escrituras simples en el campo booleano no serían suficientes porque el observador del campo booleano también necesita modificar el campo (para marcar que se ha realizado la operación de una sola vez). Es un ciclo de lectura, modificación y escritura, y eso no es seguro cuando dos o más hilos realizan los mismos pasos en el momento adecuado. En esa situación, puede poner un bloqueo mutex alrededor de la operación de una sola vez (y verificación y actualización de campo booleanas), o puede usar InterlockedExchange para actualizar y probar el campo booleano sin un mutex. También podría mover la operación de una sola vez a un constructor de tipo estático y no tener que mantener los bloqueos usted mismo (aunque .NET puede usar bloqueos detrás de escena para esto).

+2

+1 pero no hay .net aquí Danny! –

+0

Y su comentario no me tiene anhelando los iniciadores estáticos integrados en roscas .......... –

+0

El problema de los "valores parciales" realmente depende del tipo de datos. IIRC Intel garantiza que escribir en un solo byte, o en un valor alineado correctamente de 2 o 4 bytes, siempre será una escritura atómica. Y no es difícil lograr que el compilador garantice esa alineación ... –

4

Acepto que escribir un booleano de un hilo y leer de otro es seguro para subprocesos. Sin embargo, tenga cuidado con incrementando - esto no es atómico y puede causar una condición de carrera decididamente no benigh en su código dependiendo de la implementación.Incrementar/Disminuir normalmente se convierte en tres instrucciones separadas de la máquina: cargar/inc/almacenar.

Esto es para lo que interviene InterlockedIncrement, InterlockedDecrement y InterlockedExchange Win32 API - para permitir que se produzcan incrementos, decrementos y cargas de 32 bits atómicamente sin un objeto de sincronización separado.

+4

A menudo está bien si solo está escribiendo un hilo. Los problemas que describe solo se aplican con múltiples hilos de escritura. –

Cuestiones relacionadas