c# - C#中基于任务的异步方法的超时模式

标签 c# .net design-patterns asynchronous async-await

据我所知,有两种可能的模式可以实现基于任务的异步方法的超时:

内置超时

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/

相关文章:

c# - Accord.NET 中的回归分析

c# - 如何正确处理 LinqToSql 类的 System.Nullable<T> 字段?

c# - 空体构造函数中的契约(Contract)前提条件

c# - 重定向Console.Write特定程序集

c# - Elasticsearch 来自数组的多个聚合

c# - 在 JSON.NET 中将整数序列化为十六进制

c# - 将 -Ex 添加到 .Net 中的类型名称

python - 生成 lambda 函数组合的组合

java - 模式和更多用于干净简单的 gui 代码

java - 对访客设计模式感到困惑