2012-08-14 6 views
12

Tengo un objeto en un hilo de trabajo, que puedo indicarle que deje de ejecutar. Puedo implementar esta usando un bool o un AutoResetEvent:AutoResetEvent frente a boolean para detener un hilo

booleano:

private volatile bool _isRunning; 

public void Run() { 
    while (_isRunning) 
    { 
     doWork(); 
     Thread.Sleep(1000); 
    } 
} 

AutoResetEvent:

private AutoResetEvent _stop; 

public void Run() { 
    do { 
     doWork(); 
    } while (!_stop.WaitOne(1000)); 
} 

El método Stop() establecería entonces _isRunning a falso, o llame _stop.Set().

Aparte de que la solución con AutoResetEvent puede detenerse un poco más rápido, ¿hay alguna diferencia entre estos métodos? ¿Es uno mejor que el otro?

+2

Usaría un temporizador, en lugar de dormir. Para finalizar, podrías simplemente detener el temporizador. – Servy

+0

He agregado algunas observaciones que pueden responder, básicamente, no creo que estos fragmentos hagan lo que usted desea. – Jodrell

Respuesta

11

C# volátil no ofrece todas las garantías. Es puede todavía leer datos obsoletos. Es mejor utilizar el mecanismo de sincronización del sistema operativo subyacente, ya que proporciona garantías mucho más sólidas.

Todo esto en gran profundidad discussed por Eric Lippert (realmente vale la pena leer), aquí es breve cita:

En C#, "volátil" significa no sólo "asegurarse de que el compilador y el fluctuación no realice ningún reordenamiento de código ni registre las optimizaciones de almacenamiento en memoria caché en esta variable ".También significa "indicar a los procesadores hacer lo que sea necesario para garantizar que estoy leyendo el valor más reciente, incluso si eso significa detener otros procesadores y hacer que sincronicen la memoria principal con sus cachés".

En realidad, ese último bit es una mentira. La verdadera semántica de las lecturas volátiles y las escrituras son considerablemente más complejas de lo que he resumido aquí; en hecho en realidad no garantizan que cada procesador detenga lo que está haciendo y actualiza el caché a/desde la memoria principal. Por el contrario, proporcionan garantías más débiles sobre cómo se acuerdan las memorias escritas antes y después de las lecturas y las escrituras entre sí. Algunas operaciones, como crear un nuevo hilo, ingresar un candado, o usando uno de los métodos de la familia de enclavamientos, presentan garantías más sólidas sobre la observación del pedido. Si desea más detalles, lea las secciones 3.10 y 10.5.3 de la especificación C# 4.0.

Francamente, te desanime a tener que hacer un campo volátil. Los campos volátiles son una señal de que estás haciendo algo francamente loco: estás intentando leer y escribir el mismo valor en dos hilos diferentes sin poner un candado en su lugar. Los bloqueos garantizan que la lectura de la memoria o modificada dentro del bloqueo sea consistente, los bloqueos garantizan que solo un hilo accede a un pedazo dado de memoria a la vez, y así activado.

+0

1 porque esto reitera el primer punto con más detalle – Jodrell

+0

Las obras volátiles bien aquí. Cualquier hilo que se escriba en uno tiene todas sus actualizaciones de memoria en la memoria principal. Cualquier hilo que lea uno tiene su memoria de trabajo actualizada desde la principal. Por lo tanto, un _write_ a un volátil (y los datos a los que hace referencia) serán vistos por cualquier _read_ de ese volátil. Dicho esto, el AutoResetEvent se ve mucho mejor en este caso. – RalphChapin

+1

@RalphChapin yo sepa en un sistemas multiprocesador sólo hay garantías débiles * * * * Todos los que los procesadores verá un valor actualizado. – oleksii

3

En mi opinión el AutoResetEvent es mejor, porque no puede olvidarse la palabra clave crucial volatile en este caso.

0

Depende si el fragmento de arriba es todo lo que está haciendo o no. El evento AutoReset, como su nombre lo sugiere, se restablece después de que se pase el WaitOne. Esto significa que podría usarlo de nuevo inmediatamente, en lugar de con el bool, teniendo que volver a establecerlo en verdadero.

1

Antes de usar la palabra clave volatile, debe leer this y, supongo, al investigar multi-threading puede leer el artículo completo http://www.albahari.com/threading/.

Explica las sutilezas de la palabra clave volatile y por qué su comportamiento puede ser inesperado.


Se habrá dado cuenta, que cuando se utiliza volatile, lecturas y escrituras pueden quedar reordenado que podría resultar en una iteración adicional en situaciones estrechamente concurrentes. En este caso, puede que tenga que esperar alrededor de un segundo extra.


Después de mirar, no creo que su código funciona por varias razones,

El "booleano:" fragmento siempre duerme durante aproximadamente un segundo, probablemente no lo desea.

El fragmento "AutoResetEvent:" no crea una instancia de _stop y, siempre se ejecutará doWork() al menos una vez.

+0

Olvidé un '!' En _stop.WaitOne(). Estos ejemplos están un poco simplificados, pero básicamente quiero ejecutar 'doWork()' periódicamente. 'DoWork()' se ejecutará hasta que se haya quedado sin trabajo, después de lo cual tendrá que esperar un tiempo para que se acumule el nuevo trabajo. – Sjoerd

+0

@Sjoerd, edité mi última parte.La precaución alrededor de 'volátil' todavía es pertinente. – Jodrell

6

Volátil no es lo suficientemente bueno, pero prácticamente siempre funcionará porque el programador del sistema operativo siempre se bloqueará. Y funcionará bien en un núcleo con un modelo de memoria fuerte, como x86, que quema mucho jugo para mantener cachés sincronizados entre los núcleos.

Entonces, lo que realmente solo importa es qué tan rápido un hilo responderá a la solicitud de detención. Es fácil de medir, simplemente inicie un cronómetro en el hilo de control y registre el tiempo después del ciclo while en el hilo del trabajador. Los resultados que miden a partir de la repetición de tomar 1000 muestras y tomando el promedio, repetido 10 veces:

volatile bool, x86:   550 nanoseconds 
volatile bool, x64:   550 nanoseconds 
ManualResetEvent, x86:  2270 nanoseconds 
ManualResetEvent, x64:  2250 nanoseconds 
AutoResetEvent, x86:  2500 nanoseconds 
AutoResetEvent, x64:  2100 nanoseconds 
ManualResetEventSlim, x86: 650 nanoseconds 
ManualResetEventSlim, x64: 630 nanoseconds 

Ten en cuenta que los resultados para bool volátil son muy poco probable que mirar que bien en un procesador con un modelo de memoria débil, como ARM o Itanium. No tengo uno para probar.

Parece ser que quiere favorecer ManualResetEventSlim, dando una buena ejecución y una garantía.

Una nota con estos resultados, se midieron con el subproceso de trabajo ejecutando un bucle en caliente, probando constantemente la condición de detención y no haciendo ningún otro trabajo. Eso no es exactamente una buena combinación con el código real, un hilo normalmente no verificará la condición de parada con tanta frecuencia. Lo que hace que las diferencias entre estas técnicas sean en gran medida inconsecuentes.

Cuestiones relacionadas