在使用 .NET async/await API 时,我遇到了一个好奇心:循环忽略了用作超时的延迟,直到我在循环内添加了短暂的延迟。这是如何运作的?这不是最直观的行为!
完整程序:
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main(String[] args)
{
Task.Run(async () =>
{
await Task.WhenAny(Loop(), Task.Delay(TimeSpan.FromSeconds(1)));
Console.WriteLine("Timed out!");
})
.Wait();
}
public static async Task Loop()
{
while(true)
{
// Commenting this out makes the code loop indefinitely!
await Task.Delay(TimeSpan.FromMilliseconds(1));
// This doesn't matter.
await DoWork();
}
}
public static async Task DoWork()
{
await Task.CompletedTask;
}
}
背景
实际的程序有while(!done)
但由于一个错误done
从未设置为true
。循环会产生很多await
来电。这Task.WhenAny
调用是在单元测试中以防止Loop()
悬挂。如果我在大多数情况下故意引入错误,测试确实会超时,但有时它仍然会挂起。
建议的解决方法不需要Task.Delay
in Loop()
bool completedOnTime = Task.Run(() => Loop()).Wait(TimeSpan.FromSeconds(1));
这会开始一个新线程执行Loop()
方法。
相关问题
我什么时候会使用Task.Yield()?
当您等待任务时,它首先检查任务是否完成,如果完成,它只是继续执行并且永远不会返回给调用者。因此,调用await DoWork();
永远不会导致您返回到调用方法,它只会在方法中同步继续。
当你消除延迟时,你现在就相当于拥有
public static async Task Loop()
{
while(true)
{
}
}
因此循环将永远循环,而不会将控制权交还给调用者。在这种情况下,您不知道是否会返回调用者,并且您想保证不会永远循环,您可以将代码重写为
public static async Task Loop()
{
while(true)
{
var workTask = DoWork();
if(workTask.GetAwaiter().IsCompleted) //This IsCompleted property is the thing that determines if the code will be synchronous.
await Task.Yield(); //If we where syncronous force a return here via the yield.
await workTask; //We still await the task here in case where where not complete, also to observe any exceptions.
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)