前言

最近在做项目过程中使用到了如题并行方法,当时还是有点犹豫不决,因为平常使用不多, 于是借助周末时间稍微深入了下,发现我用错了,故此做一详细记录,希望对也不是很了解的童鞋在看到本文此文后不要再犯和我同样的错误。

并行遍历异步表象

这里我们就不再讲解该语法的作用以及和正常遍历处理的区别,网上文章比比皆是,我们直接进入主题,本文所演示程序在控制台中进行。可能大部分童鞋都是如下大概这样用的

Parallel.ForEach(Enumerable.Range(, ), index =>
{
Console.WriteLine(index);
});

我们采取并行方式遍历10个元素,然后结果也随机打印出10个元素,一点毛病也没有。然而我是用的异步方式,如下:

Parallel.ForEach(Enumerable.Range(, ), async index =>
{
await AsyncTask(index);
});
static async Task<int> AsyncTask(int i)
{
await Task.Delay(); var calculate = i * ; Console.WriteLine(calculate); return calculate;
}

我们只是将并行操作更改为了异步形式,然后对每个元素进行对应处理,打印无序结果,一切也是如我们所期望,接下来我再来看一个例子,经过并行异步处理后猜猜最终字典中元素个数可能或一定为多少呢?

var dicts = new ConcurrentDictionary<string, int>();

Parallel.ForEach(Enumerable.Range(, ), async index =>
{
var result = await AsyncTask(index); dicts.TryAdd(index.ToString(), result);
}); Console.WriteLine($"element count in dictionary {dicts.Count}");

如果对该并行方法没有深入了解的话,大概率都会猜错,我们看到字典中元素为0,主要原因是用了异步后引起的,为何会这样呢?我们首先从表象上来分析,当我们在控制台上对并行方法用了异步后,你会发现编译器会告警(主函数入口已用异步标识),如下:

接下来我们再来看看调用该并行异步方法的最终调用构造,如下:

public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);

第二个参数为内置委托Action,所以我们也可以看出并不能用于异步,因为要是异步至少也是Func<Task>,比如如下方法参数形式

static async Task AsyncDemo(Func<int,Task> func)
{
await func();
}

并行遍历异步本质

通过如上表象的分析我们得出并行遍历方法应该是并不支持异步(通过最终结果分析得知,表述为不能用于异步更恰当),但是在实际项目开发中我们若没有注意到该方法的构造很容易就会误以为支持异步,如我一样写完也没报错,也就草草了事。那么接下来我们反编译看下最终实际情况会是怎样的呢。

进入主函数,我们已将主函数进行异步标识,所以将主函数放在状态机中执行(状态机类,<Main>d_0),这点我们毫无保留的赞同,接下来实例化字典,并通过并行遍历异步处理元素集合并将其结果尝试放入到字典中

由上我们可以看到主函数是在状态机中运行且构造为AsyncTaskMethodBuilder,当我们通过并行遍历异步处理时每次都会实例化一个状态机类即如上<<Main>b__0>d,但我们发现此状态机的构造是AsyncVoidMethodBuilder,利用此状态机类来异步处理每一个元素,如下

最终调用AsyncTask异步方法,这里我就不再截图,同样也是生成一个此异步方法的状态机类。稍加分析想必我们已经知晓结果,AsyncTaskMethodBuilder指的就是(async task),而AsyncVoidMethodBuilder指的是(async void),所以对并行遍历异步操作是将其隐式转换为async void,而不是async task,这也和我们从其构造为Action得出的结论一致,我们知道(async void)仅限于基于事件的处理程序(常见于客户端应用程序),其他情况避免用async void,也就是说将返回值放在Task或Task<T>中。当并行执行任务时,由于返回值为void,不会等待操作完成,这也就不难解释为何字典中元素个数为0。

总结

当时并没有过多的去了解,只是想当然的认为用了异步也没出现编译报错,但是又由于没怎么用过,我还是抱着怀疑的态度,于是再深究了下,发现用法是大错特错。通过构造仅接受为Action委托,这也就意味着根本无法等待异步操作完成,之所以能接受异步索引其本质是隐式转换为(async void),从另外一个角度看,异步主要用于IO密集型,而并行处理用于CPU密集型计算,基于此上种种一定不能用于异步,否则结果你懂的。

深入了解C#(TPL)之Parallel.ForEach异步的更多相关文章

  1. C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是TAP(Task-based Asynchronous Pattern, 基于任务的异步模式)

    学习书籍: <C#本质论> 1--C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是 ...

  2. Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别

    Task C# 多线程和异步模型 TPL模型   Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task ...

  3. Parallel.ForEach , ThreadPool.QueueUserWorkItem

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  4. TPL(Task Parallel Library)多线程、并发功能

    The Task Parallel Library (TPL) is a set of public types and APIs in the System.Threading and System ...

  5. C# 多线程 Parallel.For 和 For 谁的效率高?那么 Parallel.ForEach 和 ForEach 呢?

    还是那句话:十年河东,十年河西,莫欺少年穷. 今天和大家探讨一个问题:Parallel.For 和 For 谁的效率高呢? 从CPU使用方面而言,Parallel.For 属于多线程范畴,可以开辟多个 ...

  6. Parallel.Foreach的基础知识

    微软的并行运算平台(Microsoft’s Parallel Computing Platform (PCP))提供了这样一个工具,让软件开发人员可以有效的使用多核提供的性能. Visual Stud ...

  7. Task/Parallel实现异步多线程

    代码: #region Task 异步多线程,Task是基于ThreadPool实现的 { //TestClass testClass = new TestClass(); //Action<o ...

  8. Parallel.Foreach

    随着多核时代的到来,并行开发越来越展示出它的强大威力! 使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Ta ...

  9. [译]何时使用 Parallel.ForEach,何时使用 PLINQ

    原作者: Pamela Vagata, Parallel Computing Platform Group, Microsoft Corporation 原文pdf:http://download.c ...

随机推荐

  1. vue 基于elment UI tree 组件实现带引导、提示线

    实现样式 准备工作,先实现 树状组件的基本样式 <span style="height:500px; display:block; overflow-y:auto;" cla ...

  2. [JavaWeb基础] 001.简单的JavaWeb代码和Tomcat配置部署

    简介: 其实说明白了就是J2EE应用开发,前端可以有很多的展现方式,后端由Java做逻辑运算和数据支撑.适用于创建服务器应用程序和服务,为搭建具有可伸缩性.灵活性.易维护性的商务系统提供了良好的机制. ...

  3. 离散的差分进化Discrete DE

    一般的差分算法的变异规则:Xmutation=Xr1+F(Xr2-Xr3),F为缩放因子, 离散差分进化DDE的变异规则:设每个解为K个元素的集合,则Xr2-Xr3:求出Xr2与Xr3有m个共同元素, ...

  4. 04 . Mysql主从复制和读写分离

    Mysql AB复制 ​ AB复制又称之为主从复制,用于实现数据同步,实现Mysql的AB复制时,数据库的版本尽量保持一致,如果不能保持一致,最起码从服务器的版本要高于主服务器,但是就无法实现双向复制 ...

  5. Rocket - tilelink - TLBusWrapper.to

    https://mp.weixin.qq.com/s/jSnhBzU5_ayQCg5fWAcx-g 简单介绍TLBusWrapper.to()的实现.主要介绍确定this{...}对应代码的过程. 1 ...

  6. FreeRTOS-为什么关中断之后切换进程?

    https://mp.weixin.qq.com/s/S5HBH3RTo0B2irr8sGwDdw   一. 基本问题   FreeRTOS会在关键区即taskENTER_CRITICAL()和tas ...

  7. jchdl - GSL实例 - Mux4

    https://mp.weixin.qq.com/s/hh0eExVFC6cxzpvNI1cA9A 使用门实现四选一选择器. 原理图 ​​ 参考链接 https://github.com/wjcdx/ ...

  8. Java实现 蓝桥杯 传纸条

    题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个mm行nn列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运 ...

  9. Java实现蓝桥杯算法提高 哥德巴赫猜想

    试题 算法提高 哥德巴赫猜想 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 根据所给函数(判断一个整数是否是素数),然后依托该函数,将输入N内的偶数(6-N),输出为两个素数之和( ...

  10. java实现平面点最小距离

    已知平面上若干个点的坐标. 需要求出在所有的组合中,4个点间平均距离的最小值(四舍五入,保留2位小数). 比如有4个点:a,b,c,d, 则平均距离是指:ab, ac, ad, bc, bd, cd ...