Task.WaitAll方法与Parallel.Invoke方法

Igo*_*sky 27 c# parallel-processing task

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

所以我的问题是:

  1. 为什么Parallel工作得比Task快?
  2. 我的结果是否意味着我应该使用Parallel而不是Task?
  3. 我应该在哪里使用Task和Parallel?
  4. 使用Task与Parallel相比有什么好处?
  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();
        }
    
    Run Code Online (Sandbox Code Playgroud)

这是我的处理结果: 结果

编辑:

将代码更改为如下所示:

    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();
Run Code Online (Sandbox Code Playgroud)

我的新结果:

新结果

编辑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();
}
Run Code Online (Sandbox Code Playgroud)

这是我的新结果:

在此输入图像描述

在此输入图像描述

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

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

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

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

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

Ste*_*ndl 9

编辑来自MSDN的本文:

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

与您的问题相关:

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

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

  • 看着 [this](http://msdn.microsoft.com/en-us/library/ff963549.aspx) MSDN 文章它说:“......在内部,Parallel.Invoke 创建新任务并等待它们。它使用Task 类的方法来做到这一点。这是一个例子。......” (2认同)

Wol*_*oon 5

如果您启动非通用任务(即“没有返回值的无效任务”)并立即Wait为它们使用,请Parallel.Invoke改用。你的意图对读者来说是显而易见的。

在以下情况下使用任务:

  • 你没有立即等待
  • 你需要返回值
  • 您需要为调用的方法提供参数
  • 你需要TaskCreationOptions功能
  • 您需要CancellationTokenTaskScheduler功能但不想使用ParallelOptions
  • 基本上,如果你想要更多的选择或控制

是的,您可以绕过其中的一些,例如,Parallel.Invoke(() => p.OpWithToken(CancellationToken)但这会混淆您的意图。Parallel.Invoke是为了使用尽可能多的 CPU 能力做一堆工作。它完成了,它不会死锁,你事先知道这一点。


不过你的测试很可怕。危险信号是您的长时间操作是等待 3000 毫秒,但您的测试花费的时间不到十分之一毫秒。

Task.Factory.StartNew(() => p.SomeLongOperation());
Run Code Online (Sandbox Code Playgroud)

StartNew 接受一个Action,并在一个新的main 中 执行它Task。该操作() => SomeLongOperation()创建一个子任务 Task。此子任务创建(未完成)后,调用SomeLongOperation()返回,Action完成。所以 Task在十毫秒后已经完成,而你没有参考的两个子任务仍在后台运行。Parallel 路径还创建了两个子任务,它根本不跟踪并返回。

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