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

查看:781
本文介绍了基于任务的异步方法在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使用没有内置的超时? / p>

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?

免责声明:

当我们谈论一个任务在取消状态,我们的意思是我们取消操作。当我们谈论取消时,这可能不是这种情况,因为我们只要在指定的间隔后完成任务即可。这在下面的Stephan Toubs文章中讨论了为什么BCL不提供OOTB取消正在进行的操作的功能。

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

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

This is from Stephan Toubs 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天全站免登陆