C# 在循环中添加一个短延迟可以防止它无限期地循环。为什么?

C# 在循环中添加一个短延迟可以防止它无限期地循环。为什么?,c#,asynchronous,async-await,task,infinite-loop,C#,Asynchronous,Async Await,Task,Infinite Loop,在使用.NET async/await API时,我遇到了一个奇怪的问题:循环忽略了用作超时的延迟,直到我在循环中添加了一个短延迟。这是怎么回事?不是最直观的行为 完整程序: using System; using System.Threading.Tasks; public class Program { public static void Main(String[] args) { Task.Run(async () => {

在使用.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;
    }
}
背景

实际程序在(!done)时已执行
,但由于错误,
done
从未设置为
true
。循环使许多
等待
调用。
任务。当单元测试中有任何
调用以防止
Loop()
挂起时。如果我在大多数时候故意引入一个bug,测试确实超时了,但有时它仍然挂起

不需要
任务的建议解决方法。在
循环()中延迟

bool completedOnTime = Task.Run(() => Loop()).Wait(TimeSpan.FromSeconds(1));
这将执行
Loop()
方法

相关问题

您当前的
循环()
任务将永远与您的
循环,而(true)
条件:

public static async Task Loop()
{
    while(true) { } // this iteration will never end.
                    // as noted by Scott Chamberlain's answer, the caller will
                    // never regain control of this task
}

您应该考虑通过<代码>取消EngultToC/<代码>来中断您的循环。< /P>

public static async Task Loop(CancellationTokenSource cts)
{
    while (cts != null && !cts.IsCancellationRequested)
    {
        // your work you want to keep iterating until cancelled
    }
}

我从中借来帮助解释,这也符合我的建议:

当第一个任务完成时,考虑是否取消 剩余任务。如果其他任务未取消,但同时 从来没有等待过,那么他们就被抛弃了放弃的任务将运行到 完成后,其结果将被忽略。中的任何例外情况 那些被放弃的任务也将被忽略


其他资源:

当您等待任务时,它首先检查任务是否完成,如果任务完成,它只会继续执行,而不会返回调用方。因此,调用
wait 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.
    }
}

有些事情看起来不对劲,您是否考虑过在超时任务完成时使用
CancellationToken
停止
任务循环()?这里的代码闻起来很奇怪,因为您的
任务.Loop()
永远不会停止,而且我看不到一个明确的方法来停止它。您是对的,在需要任何东西的时候实际停止它是一个取消令牌。然而,问题是,我们永远没有机会发出取消请求。这是因为wait wheny将永远不会返回。@KonradJamrozik实际上问题更严重,如果将该调用移动到
var loopTask=Loop(),则永远不会返回的是
Loop
,而不是
WaitAny
;wait Task.WhenAny(loopTask,Task.Delay(TimeSpan.FromSeconds(1)))您可以在调试器中看到它。@Svek我认为取消可能有副作用的任务(控制台或文件写入,或其他一些可见的效果)不是一个好主意。假设循环条件实际上是“while(!done)”,但由于错误,“done”从未设置为true。WhenAny超时用于测试中,以防止测试在出现错误时挂起。在这种情况下,添加取消令牌检查将相当于用测试代码污染生产代码:(@Svek,但添加取消令牌源只是为了防止bug。这不是一个干净的设计。我不会反对断言,但我发现很难证明cts只是“以防万一”的,不是所需业务逻辑的一部分。@KonradJamrozik如果您的目的不是停止迭代,请帮助我了解您试图完成的任务?您可以使用
Thread.Sleep()模拟“长时间运行的流程”
,但您的问题指出该方法称为
循环
而不是
SampleLongProcess
,我希望我是有道理的。@KonradJamrozik-我明白您关于使用
cts
的观点,因为它可能被理解为用于中断迭代的错误方法。值得注意的是……您可以很容易地加入自己的论点,例如作为
done
而不是传入
cts
。对于一个含糊不清的情况,要想找到一个完整的答案有点困难。