2014-05-05 15 views
7

Ich möchte eine Operation ausführen, die nach n Millisekunden Timeout sollte. Ich habe es auf zwei Arten implementiert, eine durch Abbrechen der Operation selbst nach dem Warten von n Millisekunden, und eine durch das Übergeben einer CancellationToken-Menge, um nach n Millisekunden abzulaufen.curlingtoken timeout vs task.delay() und timeout

Ich mache mir Sorgen, dass das cancelletoken ablaufen kann, bevor das System gestartet wird. Es scheint, dass, wenn ich das Timeout selbst mit Task.Delay() implementiere, der Aufruf von Delay() erst nach dem Start meiner Operation ausgeführt wird.

Hier ist, wie ich es so mache:

public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout) 
    { 
     Task completedTask = await Task.WhenAny(task, Task.Delay(timeout)); 
     if (completedTask == task) 
     { 
      return await task; 
     } 

     throw new TimeoutException(); 
    } 

// Use it like this 
await SomeOperationAsync().TimeoutAfter(TimeSpan.FromMilliseconds(n)); 

Im Vergleich zu:

CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromMilliseconds(n)); 
await SomeOperationAsync(source.Token); 
+0

Das ist eine schwere Last, um eine CancellationTokenSource abzulaufen, bevor die Operationen überhaupt beginnen. – Paparazzi

Antwort

15

Ich bin nicht sicher, dass Ihre Sorge gerechtfertigt ist, aber ich gehe davon aus, dass es .

Das Problem mit Ihrem Code, der Task.Delay() verwendet, ist, dass Sie den Vorgang nicht tatsächlich abbrechen. Dies kann bedeuten, dass Sie Ressourcen verschwenden oder Ihre Benutzer falsch informieren (Sie sagen ihnen, dass die Operation abgelaufen ist, während die Operation noch läuft und höchstwahrscheinlich erfolgreich abgeschlossen wird).

Nun, wenn Sie sicherstellen möchten, dass die Annullierung Token tickt beginnt erst, nachdem die Operation gestartet wird, dann tun:

var source = new CancellationTokenSource(); 
var task = SomeOperationAsync(source.Token); 
source.CancelAfter(TimeSpan.FromMilliseconds(n)); 
await task; 

Wenn Sie dies oft tun, könnten Sie dies kapseln wollen Logik in einem Verfahren (vielleicht einen besseren Namen benötigen):

public static async Task WithTimeoutAfterStart(
    Func<CancellationToken, Task> operation, TimeSpan timeout) 
{ 
    var source = new CancellationTokenSource(); 
    var task = operation(source.Token); 
    source.CancelAfter(timeout); 
    await task; 
} 

Verbrauch:

await WithTimeoutAfterStart(
    ct => SomeOperationAsync(ct), TimeSpan.FromMilliseconds(n));