在循环内添加短暂的延迟可防止其无限循环。为什么?

2023-11-23

在使用 .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(使用前将#替换为@)

在循环内添加短暂的延迟可防止其无限循环。为什么? 的相关文章

随机推荐