Mat*_*zer 8 .net c# design-patterns asynchronous async-await
据我所知,有两种可能的模式来实现基于任务的异步方法的超时:
public Task DoStuffAsync(TimeSpan timeout)
Run Code Online (Sandbox Code Playgroud)
这种方法难以实现,因为实现整个调用堆栈的全局超时并不容易.例如,Web API控制器接收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();
}
Run Code Online (Sandbox Code Playgroud)
这似乎是最可靠和易于实现的模式.第一个调用者定义全局超时,如果超时,则将取消所有挂起的操作.此外,它为直接调用者提供取消令牌,内部调用将共享相同的取消令牌引用.因此,如果最高调用者超时,它将能够取消任何工作线程.
有没有我缺少的模式,或者如果我使用no内置超时开发API,我是否以正确的方式?
i3a*_*non 13
虽然您可以重复使用WithCancellation
取消和超时,但我认为这对您的需求来说太过分了.
对于async
操作超时,更简单和更清晰的解决方案await
是实际操作和使用的超时任务Task.WhenAny
.如果超时任务首先完成,则会超时.否则,操作成功完成:
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();
}
Run Code Online (Sandbox Code Playgroud)
用法:
try
{
await DoStuffAsync().WithTimeout(TimeSpan.FromSeconds(5));
}
catch (TimeoutException)
{
// Handle timeout.
}
Run Code Online (Sandbox Code Playgroud)
如果您不想抛出异常(就像我一样)它甚至更简单,只需返回默认值:
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();
}
Run Code Online (Sandbox Code Playgroud)
Yuv*_*kov 11
有没有我缺少的模式,或者如果我使用no内置超时开发API,我是否以正确的方式?
免责声明:
当我们谈论Task
处于取消状态时,我们的意思是我们在进行时取消操作.当我们谈论取消时,这可能不是这种情况,因为如果在指定的间隔之后完成任务,我们就会丢弃任务.在下面的Stephan Toubs文章中讨论了为什么BCL不提供取消正在进行的操作的OOTB特征的原因.
我现在看到的常见方法是没有内置方法,我发现自己主要用于实现取消机制.绝对是两者中更容易的,留下最高帧负责取消,同时传递内部帧取消令牌.如果您发现自己重复此模式,则可以使用已知的WithCancellation
扩展方法:
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;
}
Run Code Online (Sandbox Code Playgroud)
这是来自Stephan Toubs 如何取消不可取消的异步操作?这并不完全符合您的要求,但绝对值得一读.
该任务取消文档去指定任务取消的方法有两种:
您可以使用以下选项之一终止操作:
只需从代表返回即可.在许多情况下,这已足够; 但是,以这种方式取消的任务实例将转换为TaskStatus.RanToCompletion状态,而不是TaskStatus.Canceled状态.
通过抛出OperationCanceledException并向其传递请求取消的令牌.执行此操作的首选方法是使用ThrowIfCancellationRequested方法.以这种方式取消的任务转换为Canceled状态,调用代码可以使用该状态验证任务是否响应其取消请求
编辑
至于你使用a TimeSpan
来指定所需的间隔,请使用带有参数的CancellationTokenSource
构造函数的重载TimeSpan
:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
var task = Task.Run(() => DoStuff()).WithCancellation(cts.Token);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
10649 次 |
最近记录: |