我想运行一个应该在n毫秒后超时的操作。我已经用两种方式实现了它,一种是在等待n毫秒后自己取消操作,另一种是在n毫秒后传递一个CancellationToken设置到期。
我担心当我的系统负载过重时,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 :(得分:22)
我不确定你的担心是否合理,但我会假设它是。
使用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));