.NET 6 新特性 WaitAsync
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET 6 新特性 WaitAsync相关的知识,希望对你有一定的参考价值。
.NET 6 新特性 WaitAsync
Intro
在 .NET 6 里新增加了一个 WaitAsync
的方法,用来异步地等待一个任务完成,异步等待的时候可以指定一个 Timeout 时间或者一个取消令牌 CancellationToken
,在之前的版本中只有一个同步的 Wait
会等待任务的完成,不支持比较好的任务超时或取消处理,如果要实现的话要自己写扩展,很多开源项目甚至微软的项目里会有一个 TimeoutAfter
之类的扩展方法,有了 WaitAsync
之后就可以取代这些扩展了
Definition
新加的 WaitAsync
是一个扩展方法,定义如下:
public static Task WaitAsync(this Task task, TimeSpan timeout);
public static Task WaitAsync(this Task task, CancellationToken cancellationToken);
public static Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken);
// 泛型版本
public static Task<TResult> WaitAsync<TResult>(this Task<TResult> task, TimeSpan timeout);
public static Task<TResult> WaitAsync<TResult>(this Task<TResult> task, CancellationToken cancellationToken);
public static Task<TResult> WaitAsync<TResult>(this Task<TResult> task, TimeSpan timeout, CancellationToken cancellationToken);
Timeout Sample
来看一个 WaitAsync
Timeout 的 使用示例:
var tasks = new List<Task>();
tasks.AddRange(new[]
{
Task.Delay(TimeSpan.FromSeconds(5)),
Task.Delay(TimeSpan.FromSeconds(8)),
Task.Delay(TimeSpan.FromSeconds(6))
});
Task task = Task.WhenAll(tasks);
try
{
await task.WaitAsync(TimeSpan.FromSeconds(3));
}
catch (TimeoutException)
{
Console.WriteLine(nameof(TimeoutException));
}
finally
{
Console.WriteLine(string.Join(",", tasks.Select(t => t.Status.ToString())));
Console.WriteLine(task.Status);
}
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine(string.Join(",", tasks.Select(t => t.Status.ToString())));
Console.WriteLine(task.Status);
上面是一个使用 Timeout
的一个示例,当 Timeout 时间到了之后 Task 还没完成就会 throw 一个 TimeoutException
,但是并不会影响原来的任务继续执行,除非自己能够在 exception 的时候将原来的 Task 给中止,上面示例的输出结果如下:
TimeoutException
WaitingForActivation,WaitingForActivation,WaitingForActivation
WaitingForActivation
RanToCompletion,RanToCompletion,RanToCompletion
RanToCompletion
可以看到,即使发生了 Timeout 也不会影响原来 Task 的执行
Cancellation Sample
接着来看一下 CancellationToken
的使用示例
var cts = new CancellationTokenSource();
var tasks = new List<Task>();
tasks.AddRange(new[]
{
Task.Delay(TimeSpan.FromSeconds(4)),
Task.Delay(TimeSpan.FromSeconds(8)),
Task.Delay(TimeSpan.FromSeconds(6))
});
var task = Task.WhenAll(tasks);
try
{
cts.CancelAfter(TimeSpan.FromSeconds(5));
await task.WaitAsync(cts.Token);
}
catch (TaskCanceledException)
{
Console.WriteLine("Task cancelled");
}
finally
{
Console.WriteLine(string.Join(",", tasks.Select(t => t.Status.ToString())));
Console.WriteLine(task.Status);
}
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine(string.Join(",", tasks.Select(t => t.Status.ToString())));
Console.WriteLine(task.Status);
输出结果如下:
Task cancelled
RanToCompletion,WaitingForActivation,WaitingForActivation
WaitingForActivation
RanToCompletion,RanToCompletion,RanToCompletion
RanToCompletion
使用 CancellationToken
的时候抛出的异常是 TaskCanceledException
,而不是前面的 TimeoutException
而抛异常的行为和前面一样,并不会影响原来 Task 的状态
Another Sample
我们再来看一个既使用 Timeout 又使用取消令牌的一个示例吧
try
{
await Task.Delay(TimeSpan.FromSeconds(5))
.WaitAsync(TimeSpan.FromSeconds(3), CancellationToken.None);
}
catch(Exception ex)
{
Console.WriteLine(ex.GetType().Name);
}
try
{
using var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(5))
.WaitAsync(TimeSpan.FromSeconds(10), cancellationTokenSource.Token);
}
catch(Exception ex)
{
Console.WriteLine(ex.GetType().Name);
}
输出结果如下:
TimeoutException
TaskCanceledException
可以看出来哪一个条件 WaitAsync
的条件先满足,抛出的就是哪一个对应的异常,两个异常都没有的就可以正常结束
More
WaitAsync
方法可以解决很多需要等待或者设置 Timeout 的场景,官方支持了这个 API 以后很多 TimeoutAfter
/WithCancellationToken
之类的扩展方法可以去掉了
WaitAsync
抛出的异常需要针对处理,如果是 Timeout 则是TimeoutException
如果是CancellationToken
Cancel 引发的异常则是TaskCanceledException
References
https://github.com/dotnet/runtime/pull/48842
https://github.com/dotnet/runtime/blob/v6.0.0-preview.7.21377.19/src/libraries/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs
https://github.com/WeihanLi/SamplesInPractice/blob/master/net6sample/WaitAsyncSample/Program.cs
以上是关于.NET 6 新特性 WaitAsync的主要内容,如果未能解决你的问题,请参考以下文章
SemaphoreSlim.WaitAsync 在尝试块之前/之后