Task.Factory.StartNew不会等待任务完成?

der*_*khh 4 c# task-parallel-library async-await

我发现以下代码实际上不会等待任务client.SendAsync()如果我使用实现:

taskList.Add(Task.Factory.StartNew(() => new Program().Foo()));
Run Code Online (Sandbox Code Playgroud)

如果我将其更改Task.Factory.StartNew()为just new Program().Foo()或者Task.Run(() => new Program.Foo()它将正确输出一些信息.两者有什么不同?

internal class Program
{
    private async Task Foo()
    {
        while (true)
        {
            var client = new HttpClient();
            var requestMessage = new HttpRequestMessage(HttpMethod.Head, "http://www.google.com");
            HttpResponseMessage response = await client.SendAsync(requestMessage);
            Console.WriteLine(response.RequestMessage.RequestUri.ToString());
        }
    }

    private static void Main(string[] args)
    {
        var taskList = new List<Task>();

        // This won't output anything.
        taskList.Add(Task.Factory.StartNew(() => new Program().Foo()));

        // This will.
        taskList.Add(Task.Run(() => new Program().Foo()));

        // So does this.
        taskList.Add(new Program().Foo());

        Task.WaitAll(taskList.ToArray());
    }
}
Run Code Online (Sandbox Code Playgroud)

基于这篇MSDN文章,它似乎Task.Run(someAction);相当于Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

但即使我将代码更改为此代码,它也不会输出任何内容.为什么?

internal class Program
{
    private async Task Foo()
    {
        while (true)
        {
            var client = new HttpClient();
            var requestMessage = new HttpRequestMessage(HttpMethod.Head, "http://www.google.com");
            HttpResponseMessage response = await client.SendAsync(requestMessage);
            Console.WriteLine(response.RequestMessage.RequestUri.ToString());
        }
    }

    private static void Main(string[] args)
    {
        var taskList = new List<Task>();
        taskList.Add(Task.Factory.StartNew(() => new Program().Foo(), CancellationToken.None,
            TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
        //taskList.Add(Task.Run(() => new Program().Foo()));
        //taskList.Add(new Program().Foo());
        Task.WaitAll(taskList.ToArray());
    }
}
Run Code Online (Sandbox Code Playgroud)

Yuv*_*kov 13

问题在于Task.Factory.StartNew不是"任务感知" 的事实.意思是,方法调用的返回类型StartNew实际上是一个Task<Task>.这意味着你只是在等待外部任务完成,而不是内部任务.

一个简单的解决方案是使用该TaskExtensions.Unwrap()方法:

private static void Main(string[] args)
{
    var taskList = new List<Task>();
    taskList.Add(Task.Factory.StartNew(() => new Program().Foo()).Unwrap());

    Task.WaitAll(taskList.ToArray());
}
Run Code Online (Sandbox Code Playgroud)

Task.Run确实有效,因为它是"任务感知".它有一个重载Func<Task>,它在内部调用Unwrap你,只返回内部任务.