Task.WaitAll方法与Parallel.Invoke方法
我有示例代码来比较Parallel方法和Task方法的处理时间。 这个实验的目标是了解它们是如何工作的。
所以我的问题是:
- 为什么Parallel工作得比Task快?
- 我的结果是否意味着我应该使用Parallel而不是Task?
- 我应该在哪里使用Task和Parallel?
- 使用Task与Parallel相比有什么好处?
-
Task只是ThreadPool.QueueUserWorkItem方法的包装吗?
public Task SomeLongOperation() { return Task.Delay(3000); } static void Main(string[] args) { Program p = new Program(); List tasks = new List(); 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替换为第二个时,情境已经被更改为cardinally。 现在Parallel比较慢。 这让我想到了我估计的不正确性。 我改变代码看起来像这样:
Program p = new Program(); Task[] tasks = new Task[2]; Stopwatch sw = null; for (int i = 0; i p.SomeLongOperation(), () => p.SomeLongOperation()); string res = sw.Elapsed.ToString(); Console.WriteLine("Parallel invoke results: " + res); sw.Stop(); } for (int i = 0; i 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方法的包装吗?
欢迎提供任何可以澄清这些问题的有用信息。
编辑来自MSDN的 本文 :
Parallel和Task都是ThreadPool的包装器。 并行调用也等待所有任务完成。
与您的问题相关:
使用Task,Parallel或ThreadPool取决于执行并行任务时需要的控制粒度。 我个人习惯了Task.Factory.StartNew()
,但那是个人观点。 这与ThreadPool.QueueUserWorkItem()
附加信息:由于内部初始化,对Parallel.Invoke()和Task.Factory.StartNew()的第一次调用可能会变慢。
如果您启动非通用任务(即“void任务没有返回值”)并立即Wait
它们,请改为使用Parallel.Invoke
。 您的意图立即向读者清楚。
使用任务如果:
- 你不要马上等
- 你需要回报价值
- 你需要为被调用的方法提供参数
- 您需要
TaskCreationOptions
function - 您需要
CancellationToken
或TaskScheduler
function,并且不想使用ParallelOptions
- 基本上,如果你想要更多的选择或控制
是的,您可以解决其中的一些问题,例如Parallel.Invoke(() => p.OpWithToken(CancellationToken)
但这会混淆您的意图Parallel.Invoke(() => p.OpWithToken(CancellationToken)
用于使用尽可能多的CPU功率进行大量工作。完成后,它不会死锁,你事先就知道了。
你的测试很可怕。 红旗将是你的长动作是等待3000毫秒,但你的测试需要不到十分之一毫秒。
Task.Factory.StartNew(() => p.SomeLongOperation());
StartNew接受一个Action
,并在一个新的主 Task
执行它。 action () => SomeLongOperation()
创建一个子 Task
。 创建此子任务(未完成)后,对SomeLongOperation()
的调用将返回,并且Action已完成。 所以主要 Task
已经在十分之一毫秒后完成,而你没有参考的两个子任务仍然在后台运行。 并行路径还创建两个子任务,它根本不跟踪,并返回。
正确的方法是tasks[0] = p.SomeLongOperation();
,它为数组分配一个正在运行的任务。 然后WaitAll
检查完成此任务。