在循环内添加短暂的延迟可防止其无限循环.为什么? [英] Adding a short delay inside a loop prevents it from looping indefinitely. Why?

查看:90
本文介绍了在循环内添加短暂的延迟可防止其无限循环.为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用.NET async/await API时,我遇到了一个好奇:循环忽略了用作超时的延迟,直到我在循环内部添加了短暂的延迟.这是如何运作的?不是最直观的行为!

While using the .NET async/await API I ran into a curiosity: a loop was ignoring a delay used as a timeout, until I added a short delay inside the loop. How does this work? Not the most intuitive behavior!

完整程序:

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()挂起.如果我在大多数情况下故意引入错误,则测试确实会超时,但有时它仍会挂起.

The actual program has while(!done) but due to a bug done is never set to true. The loop makes many await calls. The Task.WhenAny call is in a unit test to prevent Loop() from hanging. If I introduce a bug on purpose most of the time the test indeed times out, but sometimes it still just hangs.

建议的解决方法,不需要 Loop()

Suggested workaround that doesn't require Task.Delay in Loop()

bool completedOnTime = Task.Run(() => Loop()).Wait(TimeSpan.FromSeconds(1));

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

相关问题

何时使用Task.Yield()?

推荐答案

当您等待任务时,它首先检查任务是否完成,如果完成,它将继续执行,并且永不返回调用方.因此,对 await DoWork(); 的调用将永远不会导致您返回到调用方法,而只会在该方法中同步继续.

when you await a Task it first checks to see if the task is complete, if it is complete it just continues the execution and never returns to the caller. Because of this the call to await DoWork(); will never cause you to return to the calling method, it will just synchronously continue on in the method.

消除延迟后,现在相当于

When you remove the delay you now have the equivalent of having

public static async Task Loop()
{
    while(true)
    {
    }
}

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

so the loop will loop forever without ever giving control back to the caller. In situations like this where you don't know if you will be returning to the caller or not and you want to guarantee you don't loop forever you could rewrite your code as

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.
    }
}

这篇关于在循环内添加短暂的延迟可防止其无限循环.为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆