Task.WaitAll 方法与 Parallel.Invoke 方法

问题描述 投票:0回答:2

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

所以我的问题是:

  1. 为什么并行比任务运行得更快?
  2. 我的结果是否意味着我应该使用 Parallel 而不是 Task?
  3. 我应该在哪里使用Task,在哪里使用Parallel?
  4. 与并行相比,使用任务有什么好处?
  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();
        }
    

这是我的处理结果: 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 为第二个时,情况发生了根本性的改变。现在并行速度更慢。这让我想到我的估计是错误的。我将代码更改为如下所示:

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。与并行相比,使用任务有什么好处?

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

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

c# parallel-processing task
2个回答
11
投票

MSDN编辑本文

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

与您的问题相关:

使用

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

其他信息:由于内部初始化,第一次调用

Parallel.Invoke()
Task.Factory.StartNew()
可能会较慢。


7
投票

如果您启动非通用任务(即“没有返回值的空任务”)并立即为它们使用

Wait
,请改用
Parallel.Invoke
。读者会立即明白您的意图。

在以下情况下使用任务:

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

是的,您可以绕过其中一些,例如

Parallel.Invoke(() => p.OpWithToken(CancellationToken)
但这会混淆你的意图。
Parallel.Invoke
用于使用尽可能多的 CPU 能力来完成大量工作。它完成了,不会陷入僵局,而且你提前知道这一点。


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

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

StartNew 接受一个

Action
,并在新的 main
Task
中执行它。操作
() => SomeLongOperation()
创建一个 子任务
Task
。此子任务创建(未完成)后,对
SomeLongOperation()
的调用返回,并且Action完成。因此 main
Task
在第十毫秒后就已完成,而您没有引用的两个子任务 仍在后台运行。 并行路径还会创建两个子任务,但它根本不跟踪这两个子任务,并返回。

正确的方法是

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

© www.soinside.com 2019 - 2024. All rights reserved.