【问题标题】:Transforming async lambda only throwing an OperationCanceledException to synchronous variant转换异步 lambda 仅将 OperationCanceledException 抛出到同步变体
【发布时间】:2023-01-12 18:23:50
【问题描述】:
我在.NET 6 进行了这个小测试,效果很好(它是绿色的)。
Func<CancellationToken, Task> taskFactory = async token => throw new OperationCanceledException();
Assert.True(taskFactory(CancellationToken.None).IsCanceled);
然而,编译器(理所当然?)抱怨:warning CS1998: This async method lacks 'await' operators and will run synchronously。我想不出将其转换为同步变体的方法。
我为 lambda 尝试了这两个选项
- 没有异步:
token => throw new OperationCanceledException()。我很清楚,这只会将异常直接抛出到堆栈上,而不是将其包装在任务中,但这是 IDE 的建议。
-
token => Task.FromException(new OperationCanceledException())。这转到IsFaulted而不是IsCanceled。
将其转换为同步变体的正确方法是什么?
【问题讨论】:
标签:
c#
task-parallel-library
【解决方案1】:
您可以使用 Task.FromCanceled 方法:
Func<CancellationToken, Task> taskFactory = token => Task.FromCanceled(token);
【解决方案2】:
您可以等待完成的任务:
Func<CancellationToken, Task> taskFactory = async token =>
{
await Task.CompletedTask;
throw new OperationCanceledException();
};
Assert.True(taskFactory(CancellationToken.None).IsCanceled);
但是,您的测试确实存在竞争条件,尽管它通常很可能会成功。但是如果你把它改成这样:
Func<CancellationToken, Task> taskFactory = async token =>
{
await Task.Delay(1000);
throw new OperationCanceledException();
};
var task = taskFactory(CancellationToken.None);
Console.WriteLine(task.IsCanceled);
它将打印false。要解决这个问题,您必须等待任务完成:
Func<CancellationToken, Task> taskFactory = async token =>
{
await Task.Delay(1000);
throw new OperationCanceledException();
};
var task = taskFactory(CancellationToken.None);
Task.WaitAny(task);
Console.WriteLine(task.IsCanceled);
请注意,我正在使用 Task.WaitAny() 等待任务完成没有扔TaskCanceledException。如果您只是await task;,它当然会抛出该异常。