Comment «dormir» jusqu’à ce que le délai d’expiration ou l’annulation soit demandée dans .NET 4.0

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

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 .