将async lambda转换为同步变量,只抛出OperationCanceledException



我在.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

尝试了这两个选项
  1. without async:token => throw new OperationCanceledException()。这对我来说很清楚,这将只是抛出异常直接在堆栈上,而不是在任务中包装它,但这是IDE的建议。
  2. token => Task.FromException(new OperationCanceledException())。这是IsFaulted,而不是IsCanceled

将此转换为同步变量的正确方法是什么?

编辑:这段代码的目的是测试一些CUT是否正确处理来自taskFactory的OperationCanceledException为emitted。因此,异常仍然需要在解决方案中发出(可能是抛出)。我不是在简单地寻找一种方法来设置任务取消。

您可以等待一个已完成的任务:

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;,它当然会抛出那个异常。

您也可以使用低级api以同步的方式实现期望的结果。

TaskCompletionSource tcs = new();
Func<CancellationToken, Task> taskFactory = token => tcs.Task;

使用这种方法,您的委托保持同步(不需要使用async关键字)

CancellationTokenSource cts = new(0);
cts.Token.Register(() => tcs.TrySetCanceled());

这里我们通过一个简单的回调连接Task和cancel原语。
我们马上取消任务。

就是这样:)一个工作的例子可以在这里找到。

相关内容

  • 没有找到相关文章

最新更新