委托中Task.Delay之后的代码未执行

For*_*ibs 1 c# asynchronous async-await

我正在尝试同时处理列表的分区。对于列表中每个分区中的每个整数,我正在尝试等待该时间。

public class Program
{
    public async static void Main()
    {
        Console.WriteLine("Starting.");
        var values = Enumerable.Range(0,1000).ToList();

        await Task.WhenAll(
            from partition in Partitioner.Create(values).GetPartitions(10)
            select Task.Run(async delegate {
                Console.WriteLine("Entered");
                using (partition)
                    while (partition.MoveNext()){               
                        var delay = partition.Current;
                        await Task.Delay(delay);
                        Console.WriteLine(string.Format("Waited {0} milliseconds",delay));

                    }
            }));
        Console.WriteLine("Done");
    }
}
Run Code Online (Sandbox Code Playgroud)

在Task.Delay(delay)之后执行似乎停止:

Starting.
Entered
Entered
Waited 0 milliseconds
Entered
Entered
Entered
Entered
Entered
Entered
Entered
Entered
Run Code Online (Sandbox Code Playgroud)

Gab*_*uci 5

简短答案

更改Main定义以返回Task

public async static Task Main()
Run Code Online (Sandbox Code Playgroud)

更长的答案

异步方法实际上首先是同步运行的,直到第一个await。如果Task通过您调用的任何方法(Task.WhenAll在这种情况下)返回的that 还没有完成,则该await关键字通过将a返回Task到调用方法,并将该方法的其余部分作为续接上的方法,使事情变得异步Task

因此,这意味着您的Main方法实际上在Task.Delay()命中第一个之后就返回了Task,因为返回a 触发Task.Run了返回a Task,触发Task.WhenAll()了返回a Task。由于您的Main方法返回void,因此.NET不知道任何内容仍在运行,并且程序结束了。

如果返回a Task,则.NET将等待直到Task完成。

如果需要,可以在此处阅读有关有效Main签名的更多信息。

请注意,async方法签名仅在C#7.1+中有效。如果您不能使用C#7.1+,则可以asyncMain方法中删除并使用Task.WaitAll()代替await Task.WhenAll()(但您还必须调用.ToArray()该集合,因为WaitAll()它没有IEnumerable<Task>像接受那样的重载WhenAll())。这将阻塞主线程,但是在控制台应用程序中这并不是什么大问题。