Task.WaitAll方法与Parallel.Invoke方法

时间:2013-04-19 09:48:21

标签: c# parallel-processing task

我有示例代码来比较Parallel方法和Task方法的处理时间。这个实验的目标是了解它们是如何工作的。

所以我的问题是:

  1. 为什么Parallel工作得比Task快?
  2. 我的结果是否意味着我应该使用Parallel而不是Task?
  3. 我应该在哪里使用Task和Parallel?
  4. 与Parallel相比,使用Task有什么好处?
  5. Task是否只是ThreadPool.QueueUserWorkItem方法的包装?

        public Task SomeLongOperation()
        {
            return Task.Delay(3000);
        }
    
        static void Main(string[] args)
        {
            Program p = new Program();
            List<Task> tasks = new List<Task>();
    
            tasks.Add(Task.Factory.StartNew(() => p.SomeLongOperation()));
            tasks.Add(Task.Factory.StartNew(() => p.SomeLongOperation()));
    
            var arr = tasks.ToArray();
    
            Stopwatch sw = Stopwatch.StartNew();
            Task.WaitAll(arr);
            Console.WriteLine("Task wait all results: " + sw.Elapsed);
            sw.Stop();
    
            sw = Stopwatch.StartNew();
            Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
            Console.WriteLine("Parallel invoke results: " + sw.Elapsed);
            sw.Stop();
    
            Console.ReadKey();
        }
    
  6. 以下是我的处理结果: results

    修改

    将代码更改为如下所示:

        Program p = new Program();
        Task[] tasks = new Task[2];
    
        Stopwatch sw = Stopwatch.StartNew();
        tasks[0] = Task.Factory.StartNew(() => p.SomeLongOperation());
        tasks[1] = Task.Factory.StartNew(() => p.SomeLongOperation());
    
        Task.WaitAll(tasks);
        Console.WriteLine("Task wait all results: " + sw.Elapsed);
        sw.Stop();
    
        sw = Stopwatch.StartNew();
        Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
        Console.WriteLine("Parallel invoke results: " + sw.Elapsed);
        sw.Stop();
    

    我的新结果:

    new results

    编辑2: 当我将Parallel.Invoke替换为第一个代码并且将Task.WaitAll替换为第二个时,情境已经被更改为cardinally。现在Parallel比较慢。这让我想到了我的估计不正确。我将代码更改为:

    Program p = new Program();
    Task[] tasks = new Task[2];
    
    Stopwatch sw = null;
    for (int i = 0; i < 10; i++)
    {
        sw = Stopwatch.StartNew();
        Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
        string res = sw.Elapsed.ToString();
        Console.WriteLine("Parallel invoke results: " + res);
        sw.Stop();
    }
    
    for (int i = 0; i < 10; i++)
    {
        sw = Stopwatch.StartNew();
        tasks[0] = Task.Factory.StartNew(() => p.SomeLongOperation());
        tasks[1] = Task.Factory.StartNew(() => p.SomeLongOperation());
        Task.WaitAll(tasks);
        string res2 = sw.Elapsed.ToString();
        Console.WriteLine("Task wait all results: " + res2);
        sw.Stop();
    }
    

    以下是我的新结果:

    enter image description here

    enter image description here

    现在我可以建议这个实验更加明确。结果几乎相同。有时并行,有时任务更快。现在我的问题是:

    1。我应该在哪里使用Task和Parallel?

    2。使用Task与Parallel相比有什么好处?

    第3。 Task只是ThreadPool.QueueUserWorkItem方法的包装吗?

    欢迎任何可以澄清这些问题的有用信息。

2 个答案:

答案 0 :(得分:8)

来自this articleMSDN编辑:

Parallel和Task都是ThreadPool的包装器。并行调用也等待所有任务完成。

与您的问题相关:

使用Task,Parallel或ThreadPool取决于执行并行任务时需要的控制粒度。我个人习惯Task.Factory.StartNew(),但那是个人观点。这与ThreadPool.QueueUserWorkItem()

相关

附加信息:由于内部初始化,对Parallel.Invoke()和Task.Factory.StartNew()的第一次调用可能会变慢。

答案 1 :(得分:3)

如果您启动非通用任务(即&#34; void任务没有返回值&#34;)并立即启动Wait,请改用Parallel.Invoke。您的意图立即为读者所知。

在以下情况下使用任务:

  • 你不要马上等待
  • 您需要返回值
  • 您需要为名为
  • 的方法提供参数
  • 您需要TaskCreationOptions功能
  • 您需要CancellationTokenTaskScheduler个功能,并且不想使用ParallelOptions
  • 基本上,如果你想要更多选项或控制

是的,你可以解决其中一些问题,例如: Parallel.Invoke(() => p.OpWithToken(CancellationToken)但这会混淆你的意图。 Parallel.Invoke用于尽可能多地使用CPU功率进行大量工作。它完成了,它没有死锁,你提前知道了。

你的测试很可怕。红旗将是你的长动作是等待3000毫秒,但你的测试只需不到十分之一毫秒。

Task.Factory.StartNew(() => p.SomeLongOperation());

StartNew需要Action,并在新的 Task中执行此操作。动作() => SomeLongOperation()会创建子任务 Task。在此子任务创建(未完成)后,对SomeLongOperation()的调用将返回,操作完成。所以 main Task已经在十分之一毫秒后完成,而你没有引用的两个子任务仍然在后台运行。并行路径也是创建两个子任务,它根本不跟踪,然后返回。

正确的方法是tasks[0] = p.SomeLongOperation();,它为数组分配一个正在运行的任务。然后WaitAll检查完成此任务。