在我当前的项目中,我有一段代码,在将其简化到我遇到问题的地方之后,看起来像这样:
private async Task RunAsync(CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask();
}
private Task<bool> FakeTask()
{
return Task.FromResult(false);
}
如果我不等待就使用这段代码,我最终还是会阻塞:
// example 1
var task = RunAsync(cancel); // Code blocks here...
... // Other code that could run while RunAsync is doing its thing, but is forced to wait
await task;
// example 2
var task = RunAsync(cancelSource.Token); // Code blocks here...
cancelSource.Cancel(); // Never called
在实际项目中,我实际上并没有使用FakeTask,通常会有一些我在等待的Task.Delay,所以代码大部分时间实际上并没有阻塞,或者只是有限的阻塞迭代次数。
但是,在单元测试中,我使用的模拟对象几乎可以完成 FakeTask 的功能,因此当我想查看 RunAsync 是否以我期望的方式响应其 CancellationToken 被取消时,我被卡住了.
我发现我可以通过在 RunAsync 的顶部添加例如 await Task.Delay(1)
来解决这个问题,以强制它真正异步运行,但这感觉有点老套。有更好的选择吗?
最佳答案
您对 await
的作用有一个不正确的印象。 await
的含义是:
- 检查可等待对象是否完整。如果是,则获取其结果并继续执行协程。
- 如果未完成,则将当前方法的剩余部分注册为等待对象的延续,并通过将控制权返回给调用者来挂起协程。 (请注意,这使其成为一个半协程。)
在你的程序中,“假”等待总是完成的,所以协程永远不会挂起。
Are there better alternatives?
如果您的控制流逻辑要求您暂停协程,则使用 Task.Yield
。
关于c# - 异步方法阻塞未等待的任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55243987/