我有示例代码来比较并行方法和任务方法的处理时间。这个实验的目标是了解它们是如何工作的。
所以我的问题是:
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 为第二个时,情况发生了根本性的改变。现在并行速度更慢。这让我想到我的估计是错误的。我将代码更改为如下所示:
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。与并行相比,使用任务有什么好处?
3. Task 只是 ThreadPool.QueueUserWorkItem 方法的包装吗?
欢迎任何可以澄清这些问题的有用信息。
如果您启动非通用任务(即“没有返回值的空任务”)并立即为它们使用
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
检查此任务是否完成。