2011-01-21 11 views
22

Tengo un programa C++ en Linux (CentOS 5.3) generando múltiples hilos que están en un ciclo infinito para realizar un trabajo y dormir durante ciertos minutos. Ahora tengo que cancelar los hilos en ejecución en caso de que entre una nueva notificación de configuración y empiece de nuevo el nuevo conjunto de hilos, para lo cual he usado pthread_cancel. Lo que observé fue que los hilos no se detenían incluso después de recibir la indicación de cancelación, incluso algunos subprocesos estaban apareciendo después de que se había completado la suspensión.Cancelando un hilo usando pthread_cancel: buena práctica o malo

Como no se deseaba el comportamiento, el uso de pthread_cancel en el escenario mencionado genera dudas sobre si se trata de una práctica buena o mala.

Comente el uso de pthread_cancel en el escenario mencionado anteriormente.

Respuesta

39

En general, la cancelación de un hilo no es una buena idea. Siempre que sea posible, es mejor tener un indicador compartido, que los subprocesos utilicen para salir del ciclo. De esta forma, permitirá que los hilos realicen cualquier limpieza que puedan necesitar antes de salir realmente.

En el problema de los hilos que no se cancelan realmente, la especificación POSIX determina un conjunto de puntos de cancelación (man 7 pthreads). Los hilos solo se pueden cancelar en esos puntos. Si su bucle infinito no contiene un punto de cancelación, puede agregar uno llamando al pthread_testcancel. Si se ha llamado al pthread_cancel, entonces se actuará en este punto.

+5

+1 para evitar la cancelación, es la manera más rápida hacia fugas de memoria y cosas peores. Pregunte educadamente en su lugar :) –

+1

Parece que tengo que cambiar la lógica para la terminación del hilo usando un indicador compartido. Pero en otra nota, mi programa tenía hilos con el estado de cancelar configurado en ASINCRONOS, que, creo, está relacionado con la terminación inmediata de los hilos después de llamar a los respectivos manejadores de limpieza. – Mandar

+0

@ user584631: la página de manual de pthreads dice que cuando el modo está configurado como asíncrono, la cancelación puede ser inmediata, pero el sistema no está obligado a hacerlo. –

9

Si está escribiendo un código C++ seguro de excepción (consulte http://www.boost.org/community/exception_safety.html), entonces su código está listo para la cancelación del hilo. glibs throws C++ exception on thread cancel, para que sus destructores puedan realizar la limpieza adecuada.

+3

If el OP sabe que nunca más necesitarán ejecutar otra implementación de pthreads, eso está bien, pero recomiendo NO confiar en que la cancelación de subprocesos se implemente a través de excepciones. El uso de una bandera mueve la salida del hilo fuera del ámbito de "cosas ocultas" y en el código que puede ver, lo que facilita a los encargados posteriores.Tengo experiencia con este tema en particular desde hace años y he llegado a la conclusión de que lo mejor es evitar la cancelación, porque siempre hay ALGO que te hace tropezar cuando tienes la cancelación en la mezcla. –

+1

Esta publicación del blog describe por qué RAII no se puede componer exactamente con la cancelación: https://skaark.wordpress.com/2010/08/26/pthread_cancel-considered-harmful/ – erenon

+1

@erenon Buen artículo, pero es solo la mitad de la historia. Uno puede deshabilitar la cancelación de subprocesos en destructores. –

0

Usaría boost :: asio.

Algo así como:

struct Wait { 
    Wait() : timer_(io_service_), run_(true) {} 

    boost::asio::io_service io_service_; 
    mutable boost::asio::deadline_timer timer_; 
    bool run_; 
}; 

void Wait::doWwork() { 
    while (run) { 
    boost::system::error_code ec; 
    timer_.wait(ec); 
    io_service_.run(); 
    if (ec) { 
     if (ec == boost::asio::error::operation_aborted) { 
     // cleanup 
     } else { 
     // Something else, possibly nasty, happened 
     } 
    } 
    } 
} 

void Wait::halt() { 
    run_ = false; 
    timer_.cancel(); 
} 

Una vez que tienes la cabeza alrededor de ella, asio es una herramienta maravillosa.

0

Puede hacer el equivalente al siguiente código.

#include <pthread.h> 
#include <cxxabi.h> 
#include <unistd.h> 
... 
void *Control(void* pparam) 
{ 
    try 
    { 
     // do your work here, maybe long loop 
    } 
    catch (abi::__forced_unwind&) 
    { // handle pthread_cancel stack unwinding exception 
     throw; 
    } 
    catch (exception &ex) 
    { 
     throw ex; 
    } 
} 

int main() 
{ 
    pthread_t tid; 
    int rtn; 
    rtn = pthread_create(&tid, NULL, Control, NULL); 

    usleep(500); 
    // some other work here 

    rtn = pthtead_cancel(tid); 
} 
Cuestiones relacionadas