在循环内添加一个短延迟可防止它无限循环.为什么?

Kon*_*zik 3 c# asynchronous task infinite-loop async-await

在使用.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

背景

实际的程序有,while(!done)但由于一个错误done从未设置为true.循环进行多次await调用.该Task.WhenAny呼叫是在单元测试中以防止Loop()挂起.如果我故意引入一个bug,大多数时候测试确实会超时,但有时候它仍然会挂起.

建议的解决办法,不需要Task.DelayLoop()

bool completedOnTime = Task.Run(() => Loop()).Wait(TimeSpan.FromSeconds(1));
Run Code Online (Sandbox Code Playgroud)

这将启动执行该Loop()方法的新线程.

相关问题

我什么时候会使用Task.Yield()?

Sco*_*ain 5

等待任务时,它首先检查任务是否完成,如果完成,它只是继续执行,永远不会返回给调用者.因此,调用await DoWork();永远不会使您返回到调用方法,它将在方法中同步继续.

当您删除延迟时,您现在具有相应的延迟

public static async Task Loop()
{
    while(true)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

因此循环将永远循环,而不会将控制权交还给调用者.在这样的情况下,你不知道你是否会回到调用者,并且你想保证你不会永远循环,你可以将你的代码重写为

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.
    }
}
Run Code Online (Sandbox Code Playgroud)