I have sample code to compare processing time for Parallel approach and Task approach. The goal of this experiment is understanding of how do they work.
我有示例代码来比较并行方法和任务方法的处理时间。这个实验的目的是了解它们是如何工作的。
So my questions are:
所以我的问题是:
- Why Parallel worked faster then Task?
- 为什么并行比任务快?
- Do my results mean that I should use Parallel instead of Task?
- 我的结果是否意味着我应该使用并行而不是任务?
- Where should I use Task and where Parallel?
- 我应该在哪里使用Task,在哪里使用并行?
- What benefits of using Task in comparison to Parallel?
- 与并行相比,使用Task有什么好处?
-
Does Task is just a wrap for ThreadPool.QueueUserWorkItem method?
do任务只是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(); }
Here are my processing results:
以下是我的处理结果:
EDIT:
编辑:
Changed code to look like this:
更改代码如下:
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();
My new results:
我的新结果:
EDIT 2: When I replaced code with Parallel.Invoke to be first and Task.WaitAll to be second the situation has been changed cardinally. Now Parallel is slower. It makes me think of incorrectness of my estimates. I changed code to look like this:
编辑2:当我用并行代码替换代码时。调用以成为第一和任务。等待第二的情况已经改变了基数。现在平行慢。它使我想到我的估计是错误的。我将代码更改为如下所示:
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();
}
And here are my new results:
下面是我的新结果:
Now I can suggest that this experiment is much more clear. The results are almost the same. Sometimes Parallel and sometimes Task is faster. Now my questions are:
现在我可以提出这个实验要清楚得多。结果几乎是一样的。有时并行,有时任务更快。现在我的问题是:
1. Where should I use Task and where Parallel?
1。我应该在哪里使用Task,在哪里使用并行?
2. What benefits of using Task in comparison to Parallel?
2。与并行相比,使用Task有什么好处?
3. Does Task is just a wrap for ThreadPool.QueueUserWorkItem method?
3所示。do任务只是ThreadPool的一个包装。QueueUserWorkItem方法?
Any helpful info that can clarify those questions are welcome.
任何有助于澄清这些问题的有用信息都是受欢迎的。
2 个解决方案
#1
6
EDIT as of this article from MSDN:
从MSDN编辑本文:
Both Parallel and Task are wrappers for ThreadPool. Parallel invoke also awaits until all tasks will be finished.
并行和任务都是ThreadPool的包装器。并行调用也将等待所有任务完成。
Related to your questions:
相关问题:
Using Task, Parallel or ThreadPool depends on the granularity of control you need to have on the execution of your parallel tasks. I'm personally got used to Task.Factory.StartNew()
, but that's a personal opinion. The same relates to ThreadPool.QueueUserWorkItem()
使用Task、Parallel或ThreadPool取决于您在执行并行任务时所需要的控制粒度。我个人已经习惯了Task.Factory.StartNew(),但这是我个人的看法。与ThreadPool.QueueUserWorkItem()相关
Additional Information: The first call to Parallel.Invoke() and Task.Factory.StartNew() might be slower due to internal initialization.
附加信息:由于内部初始化,第一次调用Parallel.Invoke()和Task.Factory.StartNew()可能会比较慢。
#2
2
If you start nongeneric Tasks(i.e. "void Tasks without a return value") and immediately Wait
for them, use Parallel.Invoke
instead. Your intent is immediately clear to the reader.
如果您启动了非通用任务(例如。“空任务没有返回值”)并立即等待它们,使用并行。调用。你的意图对读者来说是显而易见的。
Use Tasks if:
如果使用任务:
- you do not Wait immediately
- 你不需要立即等待
- you need return values
- 你需要返回值
- you need to give parameters to the methods called
- 您需要为所调用的方法提供参数
- you require
TaskCreationOptions
functionality - 你需要TaskCreationOptions功能
- you require
CancellationToken
orTaskScheduler
functionality and don't want to useParallelOptions
- 您需要取消令牌或TaskScheduler功能,并且不希望使用并行选项
- basically, if you want more options or control
- 基本上,如果你想要更多的选择或控制。
Yes, you can get around some of these, e.g. Parallel.Invoke(() => p.OpWithToken(CancellationToken)
but that obfuscates your intent. Parallel.Invoke
is for doing a bunch of work using as much CPU power as possible. It gets done, it doesn't deadlock, and you know this in advance.
是的,您可以绕过其中的一些,例如Parallel.Invoke(() => . opwithtoken(取消令牌),但这模糊了您的意图。平行的。Invoke用于使用尽可能多的CPU能力完成大量工作。它会完成,不会死锁,你提前就知道了。
Your testing is horrid though. The red flag would be that your long action is to wait 3000 milliseconds, yet your tests take less than a tenth of a millisecond.
你的测试很可怕。值得注意的是,你的长动作是等待3000毫秒,而你的测试花费的时间不到0.1毫秒。
Task.Factory.StartNew(() => p.SomeLongOperation());
StartNew takes an Action
, and executes this in a new main Task
. The action () => SomeLongOperation()
creates a subtask Task
. After this subtask is created (not completed), the call to SomeLongOperation()
returns, and the Action is done. So the main Task
is already completed after a tenth millisecond, while the two subtasks you have no reference to are still running in the background. The Parallel path also creates two subtasks, which it doesn't track at all, and returns.
StartNew执行一个操作,并在一个新的主任务中执行这个操作。action () => somlengthoperation()创建子任务。在创建了这个子任务之后(未完成),对som伸长操作()的调用将返回,并且操作已经完成。所以主任务在十分之一毫秒后就完成了,而没有引用的两个子任务仍然在后台运行。并行路径还创建两个子任务(它根本不跟踪它们)并返回。
The correct way would be tasks[0] = p.SomeLongOperation();
, which assigns a running task to the array. Then WaitAll
checks for the finishing of this task.
正确的方法是任务[0]= p. somlengthoperation ();然后等待完成这项任务。
#1
6
EDIT as of this article from MSDN:
从MSDN编辑本文:
Both Parallel and Task are wrappers for ThreadPool. Parallel invoke also awaits until all tasks will be finished.
并行和任务都是ThreadPool的包装器。并行调用也将等待所有任务完成。
Related to your questions:
相关问题:
Using Task, Parallel or ThreadPool depends on the granularity of control you need to have on the execution of your parallel tasks. I'm personally got used to Task.Factory.StartNew()
, but that's a personal opinion. The same relates to ThreadPool.QueueUserWorkItem()
使用Task、Parallel或ThreadPool取决于您在执行并行任务时所需要的控制粒度。我个人已经习惯了Task.Factory.StartNew(),但这是我个人的看法。与ThreadPool.QueueUserWorkItem()相关
Additional Information: The first call to Parallel.Invoke() and Task.Factory.StartNew() might be slower due to internal initialization.
附加信息:由于内部初始化,第一次调用Parallel.Invoke()和Task.Factory.StartNew()可能会比较慢。
#2
2
If you start nongeneric Tasks(i.e. "void Tasks without a return value") and immediately Wait
for them, use Parallel.Invoke
instead. Your intent is immediately clear to the reader.
如果您启动了非通用任务(例如。“空任务没有返回值”)并立即等待它们,使用并行。调用。你的意图对读者来说是显而易见的。
Use Tasks if:
如果使用任务:
- you do not Wait immediately
- 你不需要立即等待
- you need return values
- 你需要返回值
- you need to give parameters to the methods called
- 您需要为所调用的方法提供参数
- you require
TaskCreationOptions
functionality - 你需要TaskCreationOptions功能
- you require
CancellationToken
orTaskScheduler
functionality and don't want to useParallelOptions
- 您需要取消令牌或TaskScheduler功能,并且不希望使用并行选项
- basically, if you want more options or control
- 基本上,如果你想要更多的选择或控制。
Yes, you can get around some of these, e.g. Parallel.Invoke(() => p.OpWithToken(CancellationToken)
but that obfuscates your intent. Parallel.Invoke
is for doing a bunch of work using as much CPU power as possible. It gets done, it doesn't deadlock, and you know this in advance.
是的,您可以绕过其中的一些,例如Parallel.Invoke(() => . opwithtoken(取消令牌),但这模糊了您的意图。平行的。Invoke用于使用尽可能多的CPU能力完成大量工作。它会完成,不会死锁,你提前就知道了。
Your testing is horrid though. The red flag would be that your long action is to wait 3000 milliseconds, yet your tests take less than a tenth of a millisecond.
你的测试很可怕。值得注意的是,你的长动作是等待3000毫秒,而你的测试花费的时间不到0.1毫秒。
Task.Factory.StartNew(() => p.SomeLongOperation());
StartNew takes an Action
, and executes this in a new main Task
. The action () => SomeLongOperation()
creates a subtask Task
. After this subtask is created (not completed), the call to SomeLongOperation()
returns, and the Action is done. So the main Task
is already completed after a tenth millisecond, while the two subtasks you have no reference to are still running in the background. The Parallel path also creates two subtasks, which it doesn't track at all, and returns.
StartNew执行一个操作,并在一个新的主任务中执行这个操作。action () => somlengthoperation()创建子任务。在创建了这个子任务之后(未完成),对som伸长操作()的调用将返回,并且操作已经完成。所以主任务在十分之一毫秒后就完成了,而没有引用的两个子任务仍然在后台运行。并行路径还创建两个子任务(它根本不跟踪它们)并返回。
The correct way would be tasks[0] = p.SomeLongOperation();
, which assigns a running task to the array. Then WaitAll
checks for the finishing of this task.
正确的方法是任务[0]= p. somlengthoperation ();然后等待完成这项任务。