Dot-Net

canceltoken 超時與 task.delay() 和超時

  • September 25, 2017

我想執行一個應該在 n 毫秒後超時的操作。我已經實現了兩種方式,一種是在等待 n 毫秒後自己取消操作,另一種是傳入一個設置為在 n 毫秒後過期的 CancellationToken。

我擔心當我的系統負載過重時,cancellationtoken 可能會在操作開始之前過期。似乎如果我自己使用 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);

我不確定你的擔心是否合理,但我會假設它是合理的。

您使用的程式碼的問題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));

引用自:https://stackoverflow.com/questions/23476576