Sto scrivendo un programma che ha alcuni thread, ognuno con un ciclo while che viene eseguito fino a quando l'utente non specifica che dovrebbe fermarsi. Ho pensato ad alcuni modi per uscire dai loop, e successivamente i thread, e ho delineato questi approcci sotto.Tecniche per l'uscita/cancellazione durante loops sui thread: bool, ManualResetEvent o CancellationToken
Domande
- Ci sono pro e contro per ciascuno?
- Ci sono casi in cui si dovrebbe usare uno e non un altro?
- Ho sentito qualcuno dire che preferiscono CancellationTokens per uscire da thread. Cosa attrae di più questo approccio rispetto agli altri due?
Segue l'approccio.
Il bool
approccio
Inizialmente, avevo dichiarato un bool e solo dichiarato che i passanti corrono fino a quando l'l'utente imposta il bool false: while(running) { Thread.Sleep(10); /*do work*/ }
. Poi ho pensato se fosse completamente sicuro per i thread. Cosa succede se il compilatore ha fatto qualche ottimizzazione e ha spostato il bool in un registro. In quel caso, i thread vedrebbero valori diversi per il bool. Di conseguenza, ho contrassegnato il bool con la parola chiave volatile
per evitare ottimizzazioni del compilatore.
Il ManualResetEvent
approccio
Il mio prossimo approccio è stato quello di creare un ManualResetEvent
, e solo dire che il bool corre mentre WaitOne() è falsa: while(!mre.WaitOne(10)) {/*do work*/}
. Questo bloccherà per 10ms, quindi eseguirà il ciclo, ripetutamente fino a quando non eseguiamo mre.Set()
e le uscite del ciclo.
Il CancellationToken
approccio
Questo approccio non ho in realtà ancora provato, ma ho letto molti posti che le persone preferiscono le discussioni annullando in questo modo. È apparentemente thread-safe. Si potrebbe definire un CancellationTokenSource
, lo chiamano cts
, quindi passare cts.Token
al metodo gestito dal nuovo thread e utilizzare un'istruzione if per verificare se annullare è stato richiesto: while(!token.IsCancellationRequested) { Thread.Sleep(10); /*do work*/ }
UPDATE 1:
ho trovato un post simile che conclude che l'approccio MRE
è notevolmente più lento dell'approccio CancellationToken
. Per la piena informazioni, vedere qui: Stopping a Thread, ManualResetEvent, volatile boolean or cancellationToken
UPDATE 2:
Quando si tratta di confrontare l'approccio bool
agli altri due, Eric Lippert ha una buona risposta qui: AutoResetEvent vs. boolean to stop a thread
UPDATE 3 :
Ho trovato un'altra informazione rilevante. CancellationTokens non può essere ripristinato una volta annullato.Quindi non è l'ideale per quando si desidera annullare temporaneamente un ciclo, per riavviarlo più tardi. Per questo, MRE potrebbe essere migliore (come puoi impostare e resettare, a tuo piacimento).
L'annullamento cooperativo tramite CTS è utile soprattutto se si chiamano altri metodi che supportano anche la cancellazione, poiché consente di propagare il token nella struttura delle chiamate. –
L'oggetto MRE può anche essere passato lungo la struttura delle chiamate. E il bool può essere una proprietà pubblica, nel qual caso non sarebbe necessario alcun passaggio. – Anders
sì, ma poiché il modello di cancellazione cooperativa è uno standard che viene spinto da MS in .NET, esistono diversi metodi sulle classi BCL che supportano CancellationToken come parametro. Un esempio è Task.Delay (quindi è possibile annullare prima del completamento di un ritardo). –