Parallel.For/ForEach/InInvoke和异步方法



正如我从论坛上的答案中了解到的,最好不要使用Parallel.For/ForEach/InInvoke的异步方法。我注意到主线程上没有等待,程序只是终止。

我是否正确理解Parallel.For/ForEach/InInvoke方法不等待主线程,同步方法是如何发生的,最好不要与它们一起使用异步方法

Parallel.For.无需等待主线程

Random rnd = new Random();
async Task MyMethod()
{
while (true)
{
Console.WriteLine(rnd.Next(1, 101));
await Task.Delay(1000);
}
}
Parallel.For(1, 5, (i) => MyMethod());

Parallel.ForEach。无需等待主线程

Random rnd = new Random();
async Task MyMethod()
{
while (true)
{
Console.WriteLine(rnd.Next(1, 101));
await Task.Delay(1000); 
}
}
int[] MyArray = new int[5];
Parallel.ForEach(MyArray, (i) => MyMethod());

Parallel.Invoke。无需等待主线程

Random rnd = new Random();
async Task MyMethod()
{
while (true)
{
Console.WriteLine(rnd.Next(1, 101));
await Task.Delay(1000);
}
}
Parallel.Invoke(() => MyMethod(), () => MyMethod(), () => MyMethod());

我是否正确理解Parallel.For/ForEach/InInvoke方法不等待主线程,同步方法是如何发生的,最好不要与它们一起使用异步方法?

所有Parallel.*方法都会阻塞当前线程,直到所有工作完成。

在您的所有示例中,并行方法将简单地运行每个异步方法,直到第一个await被命中,因为就并行方法而言,这是该方法的结束。你可以做一些类似的事情:

var results = new ConcurrentBag<Task>();
Parallel.ForEach(MyArray, (i) => results.Add(MyMethod()));
await Task.WhenAll(result);

这将以并行方式启动所有方法,然后等待所有创建的任务。但这样做大多毫无意义。

异步代码主要是为了在调用某种形式的IO绑定操作时帮助解决延迟问题,或者为服务器应用程序节省一些资源。另一方面,Parallel.*主要用于加速计算绑定的操作。在大多数情况下,用例并不能很好地混合在一起。

如果需要执行异步调用列表,您也可以使用常规循环:

var result = new List<Task>();
foreach(var item in MyArray){
result.Add(MyMethod(item));
}
await Task.WhenAll(result);

这可能会更快,因为您将避免并行运行任何东西所固有的一些开销。

在进行任何类型的多线程编程时,您都需要关注线程安全Random不是线程安全的,所以即使是这个小玩具示例也不是线程安全。在尝试任何类型的多线程编程之前,我建议先阅读一些关于线程安全的知识。

最新更新