【问题标题】:Use Task.WaitAll() to handle awaited tasks?使用 Task.WaitAll() 处理等待的任务?
【发布时间】:2013-11-19 22:03:48
【问题描述】:

理想情况下,我想做的是以非阻塞模式延迟任务,然后等待所有任务完成。我尝试添加 Task.Delay 返回的任务对象,然后使用 Task.WaitAll 但这似乎无济于事。我该如何解决这个问题?

class Program
{
    public static async void Foo(int num)
    {
        Console.WriteLine("Thread {0} - Start {1}", Thread.CurrentThread.ManagedThreadId, num);

        var newTask = Task.Delay(1000);
        TaskList.Add(newTask);
        await newTask;

        Console.WriteLine("Thread {0} - End {1}", Thread.CurrentThread.ManagedThreadId, num);
    }

    public static List<Task> TaskList = new List<Task>();

    public static void Main(string[] args)
    {
        for (int i = 0; i < 3; i++)
        {
            int idx = i;
            TaskList.Add(Task.Factory.StartNew(() => Foo(idx)));
        }

        Task.WaitAll(TaskList.ToArray());
    }
}

【问题讨论】:

  • 为什么要在控制台应用程序中这样做?没有上下文(默认情况下),所以await 无论如何都会表现得很奇怪......
  • 您希望在控制台中看到什么?这与您所看到的有何不同?
  • @AndrewShepherd:我希望看到六行显示每个任务的开始和结束。但现在我只看到 Start...
  • 您的代码似乎对我有用......(在 wpf 解决方案中运行,但是,一旦结束在 GUI 线程上运行,代码仍然有效......)
  • async void 传递给 Task.Factory.StartNew 的方法是个坏主意。您无法跟踪它启动的待处理任务或捕获它在await 之后可能引发的任何异常。查看更多信息:stackoverflow.com/q/19747910/1768303

标签: c# multithreading async-await


【解决方案1】:

需要注意的是,因为 Foo 是异步的,所以它本身就是一个任务。您的示例中的任务只是启动 Foo 任务,但不要等待它。

换句话说,Task.WaitAll(TaskList.ToArray()) 只是在等待每个Task.Delay 开始,而不是等待所有这些任务完成。

这可能是您想要实现的目标:

class Program
{
    public static async Task Foo(int num)
    {
        Console.WriteLine("Thread {0} - Start {1}", Thread.CurrentThread.ManagedThreadId, num);

        var newTask = Task.Delay(1000);

        await newTask;
        Console.WriteLine("Thread {0} - End {1}", Thread.CurrentThread.ManagedThreadId, num);

    }

    public static List<Task> TaskList = new List<Task>();

    public static void Main(string[] args)
    {
        for (int i = 0; i < 3; i++)
        {
            int idx = i;

            Task fooWrappedInTask = Task.Run(() => Foo(idx));
            TaskList.Add(fooWrappedInTask);
        }

        Task.WaitAll(TaskList.ToArray());
        Console.WriteLine("Finished waiting for all of the tasks: - Thread {0}", Thread.CurrentThread.ManagedThreadId);
    }
}

我已经对此进行了测试,它会产生您想要的控制台输出。


这里的主要区别是我们调用Task.Run 而不是Task.Factory.StartNew

您可能有一个Task,它返回一个Task,它甚至可能返回另一个Task。你会认为这是一个任务“链”。

Task.Run 返回一个Task,表示链中的最终任务。当您等待它时,您正在等待任务链中的每个环节完成。

相比之下,Task.Factory.StartNew 返回一个代表链中第一个链接的任务。在您等待它之后,剩下的就是链条的其余部分等待。在Task 返回的不是另一个Task 的情况下,这很好。

【讨论】:

  • 您在这里创建嵌套任务,Task.Run 会自动解包,这是不必要的,IMO。否则,代码似乎与version I proposed 没有什么不同。不确定这是 OP 想要的。
【解决方案2】:

这是你想要达到的目标吗?

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {
        public static async Task Foo(int num)
        {
            Console.WriteLine("Thread {0} - Start {1}", Thread.CurrentThread.ManagedThreadId, num);

            await Task.Delay(1000);

            Console.WriteLine("Thread {0} - End {1}", Thread.CurrentThread.ManagedThreadId, num);
        }

        public static List<Task> TaskList = new List<Task>();

        public static void Main(string[] args)
        {
            for (int i = 0; i < 3; i++)
            {
                int idx = i;
                TaskList.Add(Foo(idx));
            }

            Task.WaitAll(TaskList.ToArray());
            Console.WriteLine("Press Enter to exit...");
            Console.ReadLine();
        }
    }
}

输出:

线程 10 - 开始 0 线程 10 - 开始 1 线程 10 - 开始 2 线程 6 - 结束 0 线程 6 - 结束 2 线程 6 - 结束 1 按 Enter 退出...

【讨论】:

  • 并非如此。这只会输出前三个“Start ...”行,不会输出“End ...”。这意味着这些任务还没有真正完成(嗯,我知道创建了新的任务对象,而不是添加到任务列表中的原始对象)。所以我真的很想找到一种可以正确表现并表明这三个任务已完成的方法。
  • @derekhh,你试过了吗?我已经发布了一个完整的例子。您会看到 Start/Start/Start,因为您并行启动任务。每个任务都有匹配的 End。还是要一个接一个地启动它们?
  • 谢谢!我刚刚注意到您还将 Foo 的返回类型从 void 更改为 Task。是的,把它改回来会看到和你写的完全一样的行为。 :)
  • @derekhh,是的,请参阅我的 cmets 来回答您关于 async void 的问题。另外,请务必阅读 Stephen Toub's blog post 关于嵌套任务的信息,它的信息量很大。
猜你喜欢
  • 2016-02-09
  • 1970-01-01
  • 2015-11-16
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 2012-03-22
  • 1970-01-01
  • 2016-09-12
相关资源
最近更新 更多