使用并行有什么区别。ForEach或Task.Run()异步启动一组任务?
版本1:
List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
DoSomething(s);
});
版本2:
List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);
在这种情况下,第二个方法将异步等待任务完成,而不是阻塞。
然而,使用Task有一个缺点。运行在一个循环-与平行。对于每一个,都会创建一个分区器,以避免创建过多的任务。的任务。Run将始终为每个项创建单个任务(因为您正在这样做),但是Parallel类批处理工作,因此您创建的任务少于总工作项。这可以提供明显更好的整体性能,特别是如果循环体每个项只有少量的工作。
如果是这种情况,你可以将这两个选项结合起来:
await Task.Run(() => Parallel.ForEach(strings, s =>
{
DoSomething(s);
}));
注意,这个也可以写成更短的形式:
await Task.Run(() => Parallel.ForEach(strings, DoSomething));
我看过平行。ForEach使用不当,我想在这个问题中举个例子会有所帮助。
当你在控制台应用程序中运行下面的代码时,你会看到任务是如何并行执行的。ForEach不会阻塞调用线程。如果你不关心结果(正的或负的),这是可以的,但如果你确实需要结果,你应该确保使用Task.WhenAll。
using System;
using System.Linq;
using System.Threading.Tasks;
namespace ParrellelEachExample
{
class Program
{
static void Main(string[] args)
{
var indexes = new int[] { 1, 2, 3 };
RunExample((prefix) => Parallel.ForEach(indexes, (i) => DoSomethingAsync(i, prefix)),
"Parallel.Foreach");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("*You'll notice the tasks haven't run yet, because the main thread was not blocked*");
Console.WriteLine("Press any key to start the next example...");
Console.ReadKey();
RunExample((prefix) => Task.WhenAll(indexes.Select(i => DoSomethingAsync(i, prefix)).ToArray()).Wait(),
"Task.WhenAll");
Console.WriteLine("All tasks are done. Press any key to close...");
Console.ReadKey();
}
static void RunExample(Action<string> action, string prefix)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"{Environment.NewLine}Starting '{prefix}'...");
action(prefix);
Console.WriteLine($"{Environment.NewLine}Finished '{prefix}'{Environment.NewLine}");
}
static async Task DoSomethingAsync(int i, string prefix)
{
await Task.Delay(i * 1000);
Console.WriteLine($"Finished: {prefix}[{i}]");
}
}
}
结果如下:
结论:
使用平行线。带有Task的ForEach将不会阻塞调用线程。如果你在乎结果,请确保等待任务。