Interruzione del thread C ++ 0x

Secondo la bozza finale del C ++ 0x, non c’è modo di richiedere una terminazione del thread. Detto questo, se necessario, dobbiamo implementare una soluzione fai-da-te.

D’altra parte boost :: thread fornisce un meccanismo per interrompere un thread in modo sicuro .

Secondo te, qual è la soluzione migliore? Progettare il proprio “meccanismo di interruzione” cooperativo o diventare nativo?

Tutte le specifiche della lingua dicono che il supporto non è integrato nella lingua. boost::thread::interrupt richiede anche un certo supporto dalla funzione thread:

Successivamente, il thread interrotto esegue uno dei punti di interruzione specificati (o se è attualmente bloccato durante l’esecuzione di uno)

cioè quando la funzione thread non dà al chiamante la possibilità di interrompere, sei ancora bloccato.

Non sono sicuro di cosa intendi con “diventare nativo” – non esiste un supporto nativo, a meno che tu non sia incantato per boost:threads .

Comunque, userei un meccanismo esplicito. Devi pensare comunque di avere abbastanza punti di interruzione, perché non renderli espliciti? Il codice extra di solito è marginale nella mia esperienza, anche se potrebbe essere necessario cambiare alcune attese da oggetti singoli a oggetti multipli, che – a seconda della libreria – potrebbero sembrare più brutti.


Si potrebbe anche tirare il “non usare le eccezioni per il stream di controllo”, ma rispetto a fare scherzi con i thread, questa è solo una linea guida.

L’utilizzo dell’handle nativo per cancellare un thread è una ctriggers opzione in C ++ in quanto è necessario distruggere tutti gli oggetti allocati nello stack. Questo era il motivo principale per cui non includevano un’operazione di annullamento.

Boost.Thread fornisce un meccanismo di interruzione, che deve essere associato a qualsiasi primitiva in attesa. Poiché questo può essere costoso come meccanismo generale, lo standard non lo ha incluso.

Dovrai implementarlo da solo. Vedi la mia risposta qui a una domanda simile su come implementare questo da solo. Per completare la soluzione, è necessario interrompere l’interruzione quando l’interruzione è vera e il thread deve catturare questa interruzione e terminare.

Non è sicuro terminare un thread, dal momento che non si avrebbe alcun controllo sullo stato di qualsiasi struttura dati in quel momento.

Se si desidera interrompere un thread in esecuzione, è necessario implementare il proprio meccanismo. IMHO se ne hai bisogno, il tuo design non è preparato per più thread.

Se vuoi solo aspettare che una discussione finisca, usa join () o un futuro.

Non è sicuro terminare preventivamente un thread perché lo stato dell’intero processo diventa indeterminato dopo quel punto. La discussione potrebbe aver acquisito una sezione critica prima di essere terminata. Quella sezione critica non verrà mai rilasciata. L’heap potrebbe diventare bloccato in modo permanente e così via.

La soluzione boost::thread::interrupt funziona chiedendo gentilmente. Interrompe solo un thread facendo qualcosa che è interrompibile, come l’attesa su una variabile di condizione Boost.Thread, o se il thread fa una di queste cose dopo che è stato chiamato l’interrupt. Anche in questo caso, il thread non viene portato senza troppe cerimonie attraverso il tritacarne come, ad esempio, fa la funzione TerminateThread di Win32, semplicemente induce un’eccezione, che, se sei stato un coder ben educato e hai usato RAII ovunque, pulirà dopo stesso e con grazia uscire dal thread.

Implementare una soluzione fai-da-te ha più senso, e in realtà non dovrebbe essere così difficile da fare. Avrai bisogno di una variabile condivisa che tu leggi / scrivi in ​​modo sincrono, indicando se il thread è stato chiesto di terminare, e il tuo thread legge periodicamente da questa variabile quando è in uno stato in cui può essere tranquillamente interrotto. Quando si desidera interrompere un thread, si scrive semplicemente in modo sincrono su questa variabile e quindi si accede al thread. Supponendo che cooperi in modo appropriato, dovrebbe notare che la variabile è stata scritta e chiusa, con il risultato che la funzione Join non si blocca più.

Se tu dovessi andare nativo, non guadagneresti nulla con questo; elimineresti semplicemente tutti i vantaggi di un meccanismo di threading OOP standard e multipiattaforma. Affinché il tuo codice sia corretto, il thread dovrebbe chiudersi in modo cooperativo, il che implica la comunicazione sopra descritta.

Ecco la mia umile implementazione di un cancellatore di thread (per C ++ 0x). Spero che sarà utile.

 // Class cancellation_point #include  #include  struct cancelled_error {}; class cancellation_point { public: cancellation_point(): stop_(false) {} void cancel() { std::unique_lock lock(mutex_); stop_ = true; cond_.notify_all(); } template  void wait(const P& period) { std::unique_lock lock(mutex_); if (stop_ || cond_.wait_for(lock, period) == std::cv_status::no_timeout) { stop_ = false; throw cancelled_error(); } } private: bool stop_; std::mutex mutex_; std::condition_variable cond_; }; // Usage example #include  #include  class ThreadExample { public: void start() { thread_ = std::unique_ptr( new std::thread(std::bind(&ThreadExample::run, this))); } void stop() { cpoint_.cancel(); thread_->join(); } private: void run() { std::cout << "thread started\n"; try { while (true) { cpoint_.wait(std::chrono::seconds(1)); } } catch (const cancelled_error&) { std::cout << "thread cancelled\n"; } } std::unique_ptr thread_; cancellation_point cpoint_; }; int main() { ThreadExample ex; ex.start(); ex.stop(); return 0; } 

La mia implementazione di thread utilizza l’idioma pimpl, e nella class Impl ho una versione per ogni sistema operativo che supporto e anche una che usa boost, quindi posso decidere quale usare quando si costruisce il progetto.

Ho deciso di creare due classi: una è Thread, che ha solo i servizi di base forniti dal sistema operativo; e l’altro è SafeThread, che eredita da Thread e ha un metodo per l’interruzione collaborativa.

Thread ha un metodo terminate () che esegue una terminazione intrusiva. È un metodo virtuale che è sovraccarico in SafeThread, dove segnala un object evento. Esiste un metodo (statico) yeld () che il thread in esecuzione dovrebbe chiamare di volta in volta; questo metodo controlla se l’object evento è segnalato e, in caso affermativo, genera un’eccezione rilevata dal chiamante del punto di ingresso del thread, terminando quindi il thread. Quando lo fa, segnala un secondo object evento in modo che il chiamante di terminate () possa sapere che il thread è stato fermato in modo sicuro.

Per i casi in cui esiste il rischio di deadlock, SafeThread :: terminate () può accettare un parametro di timeout. Se il timeout scade, chiama Thread :: terminate (), quindi uccide in modo intrusivo il thread. Questa è un’ultima risorsa quando hai qualcosa che non puoi controllare (come un’API di terze parti) o in situazioni in cui un deadlock fa più danni di perdite di risorse e simili.

Spero che questo sia utile per la tua decisione e ti fornirà un’immagine abbastanza chiara delle mie scelte progettuali. In caso contrario, posso inserire frammenti di codice per chiarire se vuoi.

Sono d’accordo con questa decisione. Ad esempio, .NET consente di interrompere qualsiasi thread di lavoro e non utilizzo mai questa funzione e non consiglio di farlo a nessun programmatore professionista. Voglio decidere da solo, quando un thread di lavoro può essere interrotto e qual è il modo per farlo. È diverso per hardware, I / O, UI e altri thread. Se il thread può essere fermato in qualsiasi posto, ciò può causare un comportamento del programma indefinito con gestione delle risorse, transazioni, ecc.