2011-01-21 2 views
22

Ho un programma C++ su Linux (CentOS 5.3) che genera più thread che sono in un ciclo infinito per eseguire un lavoro e dormire per determinati minuti. Ora devo cancellare i thread in esecuzione nel caso in cui arrivi una nuova notifica di configurazione e inizi appena un nuovo set di thread, per il quale ho usato pthread_cancel. Quello che ho osservato è che i fili non si sono fermati anche dopo aver ricevuto l'indicazione di annullamento, anche alcuni fili dormiente si stavano alzando dopo che il sonno era completato.Annullamento di un thread utilizzando pthread_cancel: buona pratica o errata

Poiché il comportamento non è stato desiderato, l'utilizzo di pthread_cancel nello scenario menzionato solleva la questione dell'ottima o cattiva pratica.

Si prega di commentare l'utilizzo di pthread_cancel nello scenario sopra citato.

risposta

39

In generale la cancellazione del thread non è una buona idea. È preferibile, quando possibile, avere un flag condiviso, utilizzato dai thread per uscire dal ciclo. In questo modo, consentirai ai thread di eseguire qualsiasi operazione di pulizia che potrebbero essere necessari prima di uscire effettivamente.

Sul problema dei thread che non si stanno effettivamente annullando, la specifica POSIX determina un set di punti di annullamento (man 7 pthreads). I thread possono essere cancellati solo in quei punti. Se il tuo loop infinito non contiene un punto di cancellazione, puoi aggiungerne uno chiamando lo pthread_testcancel. Se è stato chiamato pthread_cancel, a questo punto verrà eseguito il comando.

+5

+1 per evitare la cancellazione, è il modo più veloce per perdite di memoria, e peggio. Chiedi invece educatamente :) –

+1

Sembra che debba cambiare la logica per la terminazione del thread usando un flag condiviso. Ma in altre note il mio programma aveva thread con stato di annullamento impostato su ASYNCHRONOUS che, credo, è correlato alla chiusura immediata dei thread dopo aver chiamato i rispettivi gestori di cleanup. – Mandar

+0

@ user584631: La pagina di manuale pthreads dice che quando la modalità è impostata su asincrono, la cancellazione può essere immediata, ma il sistema non è tenuto a farlo. –

9

Se si sta scrivendo codice C++ eccezionalmente sicuro (vedere http://www.boost.org/community/exception_safety.html), il codice è naturalmente pronto per la cancellazione del thread. glibs throws C++ exception on thread cancel, in modo che i distruttori possano eseguire il clean-up appropriato.

+3

If l'OP sa che non avranno mai bisogno di eseguire un'altra implementazione di pthreads, va bene, ma io NON raccomando di fare affidamento sull'annullamento del thread implementato tramite eccezioni. Usando un flag si sposta l'uscita del thread dal regno di 'hidden stuff' e nel codice che si può vedere, rendendolo più facile per i manutentori successivi.Ho avuto esperienza con questo particolare problema per anni e ho concluso che la cosa migliore è evitare la cancellazione, perché c'è sempre QUALCOSA che ti fa inciampare quando hai annullamento nel mix. –

+1

Questo post del blog descrive esattamente il motivo per cui non è RAII componibile con cancellazione: https://skaark.wordpress.com/2010/08/26/pthread_cancel-considered-harmful/ – erenon

+1

@erenon Nizza articolo, ma è solo metà della storia. Si può disabilitare la cancellazione del thread nei distruttori. –

0

Vorrei usare boost :: asio.

Qualcosa di simile:

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 volta che hai la testa intorno ad esso, ASIO è uno strumento meraviglioso.

0

È possibile eseguire l'equivalente del codice riportato di seguito.

#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); 
}