【问题标题】:Task.Factory.StartNew won't wait for task completion?Task.Factory.StartNew 不会等待任务完成?
【发布时间】:2016-10-13 23:27:04
【问题描述】:

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

taskList.Add(Task.Factory.StartNew(() => new Program().Foo()));

如果我将其从 Task.Factory.StartNew() 更改为 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());
    }
}

基于this MSDN article,看来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());
    }
}

【问题讨论】:

  • 主题行中问题的答案 - 不,除非您要求。
  • 看看异步/等待模式。这将拓宽你在这个问题上的视野。
  • @derekhh:阅读your whole reference。您引用的部分以 When you pass an Action to Task.Run 为前缀,您的代码没有这样做。在博客文章的后面,他解释了 Task.Run 与异步委托的行为有何不同。

标签: c# async-await task-parallel-library


【解决方案1】:

问题在于Task.Factory.StartNew 不是“任务感知”的。这意味着,从您的方法调用到StartNew 的返回类型实际上是Task&lt;Task&gt;。这意味着您只是在等待外部任务完成,而不是内部任务

一个简单的解决方案是使用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());
}

Task.Run 确实有效,因为它是“任务感知”的。 It has an overload taking a Func&lt;Task&gt;,内部为你调用Unwrap,只返回内部任务。

【讨论】:

  • 单个任务:var task = Task.Factory.StartNew(something);task.Wait();
【解决方案2】:

Task.Factory.StartNew 默认不等待内部任务完成。如果你用返回值创建一个变量,你会得到这个:

Task<Task> task = Task.Factory.StartNew(() => new Program().Foo());

这意味着一旦内部任务到达 await 语句,委托就会返回。您需要调用UnWrap 方法来强制异步执行:

Task task = Task.Factory.StartNew(() => new Program().Foo()).Unwrap();
taskList.Add(task);

您发布的博文还解释了同步委托和异步委托之间的区别。

【讨论】:

    【解决方案3】:

    您的代码等待new Program().Foo() 结束,并带有Task.WaitAll(taskList.ToArray()); 语句

    但是,Foo 在返回之前不会等待client.SendAsync(requestMessage) 的结束,因为它有一个await。 await 将让方法返回Task 对象并允许线程返回下一行。

    “问题”是,由于您返回 await,调用 Foo 方法的任务在那时已经完成,甚至在您点击命令行上的输出之前(任务 Foo 方法返回,但不完整)。

    您基本上有 3 个“时间线”(我不会说线程,因为我不确定 HttpClient 是否真的启动了一个新线程,但如果有人可以确认这一点,我会编辑帖子。)和 2 个任务:

    • 你的Main 线程
    • 使用StartNew 创建的任务
    • SendAsync 任务。

    您的Main 线程正在等待StartNew 创建的任务结束,但该任务确实等待SendAsync 调用结束,因此不会等待命令行输出认为自己已完成。因为在WaitAll 中只考虑了那个,而 SendAsync 没有,所以早点返回是正常的。

    要拥有你想要的行为,你应该有类似的东西

    taskList.Add(Task.Factory.StartNew(() => new Program().Foo().Wait()));
    

    但话又说回来,开始一个新任务只是为了等待另一个任务没有多大意义,所以你最好选择taskList.Add(new Program().Foo());

    【讨论】:

      猜你喜欢
      • 2023-04-04
      • 2015-11-16
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 2021-01-26
      • 2016-01-30
      • 1970-01-01
      相关资源
      最近更新 更多