2014-05-05 89 views
7

我想運行一個應該在n毫秒後超時的操作。我已經實現了兩種方法,一種是在等待n毫秒後自行取消操作,另一種方法是傳入CancellationToken集,使其在n毫秒後過期。cancellationtoken超時vs task.delay()和超時

我很擔心,當我的系統承受沉重負載時,在操作甚至開始之前,canceltoken可能會過期。看起來,如果我使用Task.Delay()自己實現超時,那麼在我的操作開始之前,Delay()調用將不會運行。

這裏是我如何做它:

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

相比:

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

這是在操作甚至開始之前過期CancellationTokenSource的重負載。 – Paparazzi

回答

15

我不知道你的擔心是有道理的,但我會認爲這是。

您的代碼使用Task.Delay()的問題在於您並未真正取消操作。這可能意味着您正在浪費資源或誤導用戶(您告訴他們操作超時,而操作仍在運行,並且很可能會成功完成)。現在

,如果你想確保取消標記開始操作開始後才滴答作響,然後

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

如果你這樣做的時候,你可能想封裝這個邏輯到一個方法(可能需要一個更好的名字):

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

用法:

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