据我所知,有两种可能的模式可以实现基于任务的异步方法的超时:
内置超时
public Task DoStuffAsync(TimeSpan timeout)
这种方法更难实现,因为要为整个调用堆栈实现全局 超时并不容易。例如,Web API Controller 接收到一个 HTTP 请求并调用 DoStuffAsync
,而调用者想要 3 秒的全局超时。
也就是说,每个内部异步方法调用都需要减去已使用的时间...
没有内置超时
public Task DoStuffAsync(CancellationToken cancellationToken)
..........
CancellationTokenSource cancellationSource = new CancellationTokenSource();
Task timeoutTask = Task.Delay(3000);
if(await Task.WhenAny(DoStuffAsync(cancellationTokenSource), timeoutTask) == timeoutTask)
{
cancellationSource.Cancel();
throw new TimeoutException();
}
这似乎是最可靠和最容易实现的模式。第一个调用者定义了一个全局超时,如果它超时,所有挂起的操作都将被取消。此外,它向直接调用者提供取消 token ,内部调用将共享相同的取消 token 引用。因此,如果顶级调用者超时,它将能够取消任何工作线程。
整个问题
如果我使用 无内置超时 开发 API,我是否遗漏了任何模式,或者我的方法是否正确?
最佳答案
虽然您可以为取消和超时重用 WithCancellation
,但我认为这对您的需求来说有点矫枉过正。
async
操作超时的更简单、更清晰的解决方案是使用 Task.WhenAny
await
实际操作和超时任务.如果超时任务先完成,您就超时了。否则,操作成功完成:
public static async Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
return await task;
}
throw new TimeoutException();
}
用法:
try
{
await DoStuffAsync().WithTimeout(TimeSpan.FromSeconds(5));
}
catch (TimeoutException)
{
// Handle timeout.
}
如果你不想抛出异常(就像我一样),那就更简单了,只需返回默认值:
public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, timeoutTask).Unwrap();
}
关于c# - C#中基于任务的异步方法的超时模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25683980/