Quelle est la meilleure façon de dormir un certain temps, mais pouvoir être interrompu par un IsCancellationRequested
partir d’un CancellationToken
?
Je cherche une solution qui fonctionne dans .NET 4.0.
Je voudrais écrire
void MyFunc (CancellationToken ct) { //... // simulate some long lasting operation that should be cancelable Thread.Sleep(TimeSpan.FromMilliseconds(10000), ct); }
Je viens de bloguer à ce sujet ici:
CancellationToken et Thread.Sleep
en bref:
var cancelled = token.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));
Dans votre contexte:
void MyFunc (CancellationToken ct) { //... // simulate some long lasting operation that should be cancelable var cancelled = ct.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)); }
Alternativement, je pense que c’est assez clair:
Task.Delay(waitTimeInMs, cancellationToken).Wait(cancellationToken);
Pour annuler une opération asynchrone après un certain laps de temps tout en pouvant annuler l’opération manuellement, utilisez quelque chose comme:
CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; cts.CancelAfter(5000);
Cela provoquera une annulation après cinq secondes. Pour annuler l’opération vous-même, il vous suffit de passer le token
à votre méthode async et d’utiliser la méthode token.ThrowifCancellationRequested()
, où vous avez configuré un gestionnaire d’événements quelque part pour déclencher cts.Cancel()
.
Donc, un exemple complet est:
CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; cts.CancelAfter(5000); // Set up the event handler on some button. if (cancelSource != null) { cancelHandler = delegate { Cancel(cts); }; stopButton.Click -= cancelHandler; stopButton.Click += cancelHandler; } // Now launch the method. SomeMethodAsync(token);
Où stopButton
est le bouton sur stopButton
vous cliquez pour annuler la tâche en cours d’exécution
private void Cancel(CancellationTokenSource cts) { cts.Cancel(); }
et la méthode est définie comme
SomeMethodAsync(CancellationToken token) { Task t = Task.Factory.StartNew(() => { msTimeout = 5000; Pump(token); }, token, TaskCreationOptions.None, TaskScheduler.Default); }
Maintenant, pour vous permettre de travailler sur le sujet, mais aussi pour permettre l’annulation des utilisateurs, vous devrez écrire une méthode de «pompage».
int msTimeout; bool timeLimitReached = false; private void Pump(CancellationToken token) { DateTime now = DateTime.Now; System.Timer t = new System.Timer(100); t.Elapsed -= t_Elapsed; t.Elapsed += t_Elapsed; t.Start(); while(!timeLimitReached) { Thread.Sleep(250); token.ThrowIfCancellationRequested(); } } void t_Elapsed(object sender, ElapsedEventArgs e) { TimeSpan elapsed = DateTime.Now - this.readyUpInitialised; if (elapsed > msTimeout) { timeLimitReached = true; t.Stop(); t.Dispose(); } }
Notez que SomeAsyncMethod
retournera directement à l’appelant. Pour bloquer l’appelant, vous devrez également déplacer la Task
dans la hiérarchie des appels.
La meilleure solution que j’ai trouvée jusqu’à présent est la suivante:
void MyFunc(CancellationToken ct) { //... var timedOut = WaitHandle.WaitAny(new[] { ct.WaitHandle }, TimeSpan.FromMilliseconds(2000)) == WaitHandle.WaitTimeout; var cancelled = ! timedOut; }
METTRE À JOUR:
La meilleure solution à ce jour est la réponse acceptée .