6

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

  1. Ci sono pro e contro per ciascuno?
  2. Ci sono casi in cui si dovrebbe usare uno e non un altro?
  3. 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).

+0

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. –

+0

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

+1

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). –

risposta

2

Molto spesso i tuoi thread non girano in loop stretti consumando tutti i tuoi cicli di CPU, spesso stai aspettando eventi di qualche tipo, quando aspetti non puoi davvero aspettare un bool. Potresti avere un timeout sul tuo evento, attendere il timeout, controllare il bool, quindi tornare ad aspettare. Questo rende il codice sgradevole e significa anche che il thread non si chiuderà fino a quando non si verifica un timeout, o continuerai a mangiare la tua CPU controllando un bool.

Reset L'evento è ok, si può certamente lavorare con esso, ma CancelellationToken funziona bene ed è progettato proprio per questo.

+0

L'app che sto scrivendo mi richiede di verificare alcune condizioni che non rientrano nella mia app e in quanto tale non ci sono eventi o cose che posso aggiungere per assicurarmi che mi informi del cambiamento. Devo fare una specie di sondaggio. Invece di usare i cicli while, si potrebbe usare un Timer, che esegue il polling ordinatamente con intervalli regolari dal pool di thread (ho usato tale approccio laddove appropriato). Questo è un po 'al di fuori della portata di questo post, però, che è più sul confronto tra le tecniche di cancellazione. – Anders