C#中基于任务的异步方法的超时模式 [英] Timeout pattern on task-based asynchronous method in C#

查看:110
本文介绍了C#中基于任务的异步方法的超时模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

As far as I know, there're two possible patterns to implement a timeout to task-based asynchronous methods:

public Task DoStuffAsync(TimeSpan timeout)

这种方法更难实现,因为为整个调用堆栈实现全局超时并不容易.例如,Web API 控制器接收到一个 HTTP 请求并调用 DoStuffAsync,而调用者想要 3 秒的全局超时.

This approach is harder to implement because it's not easy to implement a global timeout for the entire call stack. For example, a Web API controller receives an HTTP request and it calls DoStuffAsync, and the caller wants a global timeout of 3 seconds.

也就是说,每个内部异步方法调用都需要接收已使用时间的减法...

That is, each inner async method call will need to receive the subtract of already used time...

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

这似乎是最可靠和最容易实现的模式.第一个调用者定义了一个全局超时,如果它超时,所有挂起的操作都将被取消.此外,它为直接调用者提供取消令牌,内部调用将共享相同的取消令牌引用.因此,如果顶级调用者超时,它将能够取消任何工作线程.

This seems to be the most reliable and easy to implement pattern. The first caller defines a global timeout, and if it time outs, all pending operations will be cancelled. Also, it provides a cancellation token to the immediate caller and inner calls will share the same cancellation token reference. Thus, if the top caller time outs, it will be able to cancel any working thread.

是否有任何我遗漏的模式,或者,如果我使用无内置超时开发 API,我的方法是否正确?

Is there any pattern that I'm missing or, am I in the right way if I develop APIs using the no built-in timeout?

推荐答案

是否有任何我遗漏的模式,或者,如果我使用无内置超时开发 API,我的方法是否正确?

Is there any pattern that I'm missing or, am I in the right way if I develop APIs using the no built-in timeout?

免责声明:

当我们谈论处于取消状态的 Task 时,我们的意思是我们在操作进行时取消它.当我们谈论取消时,这可能不是这种情况,因为如果任务在指定的时间间隔后完成,我们就会简单地丢弃它.关于为什么 BCL 不提供取消正在进行的操作的 OOTB 功能,这在下面的 Stephan Toubs 文章中进行了一定程度的讨论.

When we talk about a Task in a cancelled state, we mean that we cancel the operation as it proceeds. This might not be the case here when we talk about cancellation, as we simply discard the task if it completed after the specified interval. This is discussed to extent in Stephan Toubs article below as to why the BCL does not provide OOTB features of cancelling an ongoing operation.

我现在看到的常见方法是无内置方法,我发现自己主要使用这种方法来实现取消机制.这绝对是两者中更容易的,让最高帧负责取消,同时将取消令牌传递给内部帧.如果你发现自己在重复这个模式,你可以使用已知的 WithCancellation 扩展方法:

The common approach i see nowadays is the no build-in approach and the one i find myself using mostly to implement a cancelling mechanism. It is definitely the easier of the two, leaving the highest frame to be in charge of cancellation while passing the inner frames the cancellation token. If you find yourself repeating this pattern, you can use the known WithCancellation extension method:

public static async Task<T> WithCancellation<T>(
    this Task<T> task, CancellationToken cancellationToken)
{
    var cancellationCompletionSource = new TaskCompletionSource<bool>();

    using (cancellationToken.Register(() => cancellationCompletionSource.TrySetResult(true)))
    {
        if (task != await Task.WhenAny(task, cancellationCompletionSource.Task))
        {
            throw new OperationCanceledException(cancellationToken);
        }
    }

    return await task;
}

这是来自 Stephen Toub 的 我如何取消不可取消的异步操作?这并不完全符合您的要求,但绝对值得一读.

This is from Stephen Toub's How do I cancel non-cancelable async operations? which isn't exactly spot on to what you're asking, but is definitely worth a read.

任务取消文档 继续指定两种取消任务的方式:

The Task Cancellation docs go on to specify two ways of task cancellation:

您可以使用以下选项之一终止操作:

You can terminate the operation by using one of these options:

  1. 通过简单地从委托返回.在许多情况下,这已经足够了;但是,以这种方式取消的任务实例会转换为 TaskStatus.RanToCompletion 状态,而不是 TaskStatus.Canceled 状态.

  1. By simply returning from the delegate. In many scenarios this is sufficient; however, a task instance that is canceled in this way transitions to the TaskStatus.RanToCompletion state, not to the TaskStatus.Canceled state.

通过抛出 OperationCanceledException 并将请求取消的令牌传递给它.执行此操作的首选方法是使用 ThrowIfCancellationRequested 方法.以这种方式取消的任务转换到取消状态,调用代码可以使用该状态来验证任务是否响应了其取消请求

By throwing a OperationCanceledException and passing it the token on which cancellation was requested. The preferred way to do this is to use the ThrowIfCancellationRequested method. A task that is canceled in this way transitions to the Canceled state, which the calling code can use to verify that the task responded to its cancellation request

编辑

至于您担心使用 TimeSpan 指定所需的间隔,请使用 重载 CancellationTokenSource 构造函数,它接受一个 TimeSpan 参数:

As for you concern with using a TimeSpan to specify the desired interval, use the overload of CancellationTokenSource constructor which takes a TimeSpan parameter:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));

var task = Task.Run(() => DoStuff()).WithCancellation(cts.Token);

这篇关于C#中基于任务的异步方法的超时模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆