32

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

所以我的问题是:

  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();
        }
    

这是我的处理结果: 结果

编辑:

将代码更改为如下所示:

    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();

我的新结果:

新结果

编辑 2: 当我将代码替换为 Parallel.Invoke 为第一个,Task.WaitAll 为第二个时,情况发生了根本性的变化。现在 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();
}

这是我的新结果:

在此处输入图像描述

在此处输入图像描述

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

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

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

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

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

4

2 回答 2

10

MSDN的这篇文章开始编辑:

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

与您的问题相关:

使用 Task、Parallel 或 ThreadPool 取决于您对执行并行任务所需的控制粒度。我个人习惯了Task.Factory.StartNew(),但这是个人意见。同样涉及ThreadPool.QueueUserWorkItem()

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

于 2013-04-19T10:38:45.227 回答
5

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

在以下情况下使用任务:

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

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


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

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

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

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

于 2016-10-03T18:22:29.867 回答