2008-08-29 23 views
10

¿Es seguro lo siguiente?C++ Pregunta de hilo: estableciendo un valor para indicar que el hilo ha finalizado

Soy nuevo en threading y deseo delegar un proceso lento en un hilo separado en mi programa C++. Utilizando las bibliotecas Boost que he escrito algo como esto Código:

THRD = nuevo impulso :: hilo (boost :: bind (& miclase :: MyMethod, esto, & finished_flag);

Dónde finished_flag es una miembro booleano de mi clase. Cuando termina el hilo, establece el valor y el ciclo principal de mi programa comprueba si hay un cambio en ese valor. Supongo que está bien porque solo comienzo un hilo, y ese hilo es el Lo único que cambia el valor (excepto cuando se inicializa antes de iniciar el hilo) Entonces, ¿está bien, o me falta algo, y necesito usar bloqueos y mutexes, etc.

Respuesta

11

Nunca se menciona el tipo de finished_flag ...

Si se trata de una recta bool, entonces podría funcionar, pero es sin duda una mala práctica, por varias razones. Primero, algunos compiladores almacenarán en caché las lecturas de la variable finished_flag, ya que el compilador no siempre detecta el hecho de que otro subproceso lo está escribiendo. Puede evitar esto declarando el boolvolátil, pero eso nos está llevando en la dirección incorrecta. Incluso si las lecturas y escrituras están sucediendo como era de esperar, no hay nada que impida que el planificador del sistema operativo entrelaje los dos hilos a la mitad de una lectura/escritura. Eso podría no ser un problema aquí donde tienes una operación de lectura y otra de escritura en hilos separados, pero es una buena idea comenzar como quieres continuar.

Si, por otro lado, es un tipo seguro para subprocesos, como CEvent in MFC (o equivilent in boost), entonces debería estar bien. Este es el mejor enfoque: use objetos de sincronización seguros para subprocesos para la comunicación entre hilos, incluso para indicadores simples.

+0

No voy a preocuparse tanto por lo que el valor de las memorias caché del compilador como los cachés reales (T1, T2, y T3) en el procesador. La razón es que en una máquina de múltiples procesamientos, las memorias caché para cada procesador pueden no coincidir. Esa es una de las razones por las que usa la palabra clave volátil. –

+0

Sí, quizás, pero por favor: repita después de mí: "bool volátil" NO es un sustituto de la sincronización adecuada. – Thomi

7

En lugar de usar una variable miembro para indicar que el hilo está hecho, ¿por qué no utilizar un condition? Ya está utilizando las bibliotecas de impulso, y condition es parte de la biblioteca de hilos.

Compruébalo out. Permite que el hilo del trabajador 'señalice' que ha terminado, y el hilo principal puede verificar durante la ejecución si la condición ha sido señalada y luego hacer todo lo que tenga que hacer con el trabajo completado. Hay ejemplos en el enlace.

Como un caso general, no hubiera asumido que un recurso solo será modificado por el hilo. Puede que sepas para qué sirve, sin embargo, es posible que otra persona no lo haga, sin causar ningún tipo de dolor, ya que el hilo principal cree que el trabajo está hecho e intenta acceder a datos que no son correctos. Incluso podría eliminarlo mientras el subproceso de trabajo todavía lo esté utilizando y haciendo que la aplicación se cuelgue. Usar un condition lo ayudará.

Al mirar la documentación thread, también puede llamar al thread.timed_join en el hilo principal.timed_join esperará una cantidad especificada para el hilo a 'unirse' (unirse a los medios que el mensaje ha finsihed)

5

Si realmente quiere entrar en los detalles de la comunicación entre hilos a través de la memoria compartida, incluso declarar una won volátil variables No será suficiente, incluso si el compilador utiliza una semántica de acceso adecuada para garantizar que no obtenga una versión obsoleta de los datos después de verificar el indicador. La CPU puede emitir lecturas y grabaciones fuera de servicio siempre (x86 generalmente no lo hace, pero definitivamente PPC lo hace) y no hay nada en C++ 9x que permita al compilador generar código para ordenar apropiadamente los accesos a la memoria.

Herb Sutter's Effective Concurrency series tiene una visión extremadamente detallada de cómo el mundo C++ se cruza con el mundo multinúcleo/multiprocesador.

+0

Permítanme agregar una más [gran lectura] (http://www.usenix.org/event/hotpar11/tech/final_files/Boehm.pdf) que demuestra lo difícil que es determinar el resultado de un programa que no utiliza primitivas de sincronización adecuada. –

2

Tener el hilo establecer un indicador (o señalar un evento) antes de que salga es una condición de carrera. El hilo no ha regresado necesariamente al sistema operativo todavía y aún puede estar ejecutándose.

Por ejemplo, considere un programa que carga una biblioteca dinámica (pseudocódigo):

lib = loadLibrary("someLibrary"); 
fun = getFunction("someFunction"); 
fun(); 
unloadLibrary(lib); 

Y vamos a suponer que esta biblioteca utiliza su hilo:

void someFunction() { 
    volatile bool finished_flag = false; 
    thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag); 
    while(!finished_flag) { // ignore the polling loop, it's besides the point 
     sleep(); 
    } 
    delete thrd; 
} 

void myclass::mymethod() { 
    // do stuff 
    finished_flag = true; 
} 

Cuando myclass::mymethod() conjuntos finished_flag a true, myclass::mymethod() no ha regresado todavía. Por lo menos, todavía tiene que ejecutar una instrucción de "retorno" de algún tipo (si no mucho más: destructores, gestión de manejador de excepciones, etc.). Si el subproceso que ejecuta myclass::mymethod() se reemplaza antes de ese punto, someFunction() regresará al programa que realiza la llamada y el programa llamante descargará la biblioteca. Cuando se ejecuta la ejecución programada del subproceso myclass::mymethod(), la dirección que contiene la instrucción "devolver" ya no es válida y el programa se bloquea.

La solución sería someFunction() para llamar al thrd->join() antes de devolver. Esto garantizaría que el hilo haya regresado al SO y ya no se esté ejecutando.

5

no me refiero a ser presuntiva, pero parece que el propósito de su finished_flag variable es para hacer una pausa el hilo principal (en algún momento) hasta que el hilo THRD ha completado.

La forma más sencilla de hacerlo es utilizar impulso :: hilo :: unirse a

// launch the thread... 
thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag); 

// ... do other things maybe ... 

// wait for the thread to complete 
thrd.join(); 
Cuestiones relacionadas