Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >4.0中的并行计算和多线程详解(一)

4.0中的并行计算和多线程详解(一)

作者头像
vv彭
发布于 2021-01-27 02:05:51
发布于 2021-01-27 02:05:51
1.7K0
举报
文章被收录于专栏:c#学习笔记c#学习笔记

4.0中的并行计算和多线程详解(一)

转自:https://cloud.tencent.com/developer/article/1780190

并行计算部分

代码语言:txt
AI代码解释
复制
    沿用微软的写法,System.Threading.Tasks.::.Parallel类,提供对并行循环和区域的支持。 我们会用到的方法有For,ForEach,Invoke。

一、简单使用

代码语言:txt
AI代码解释
复制
    首先我们初始化一个List用于循环,这里我们循环10次。(后面的代码都会按这个标准进行循环)

Code

  1. Program.Data = new List<int>();
  2. for (int i = 0; i < 10; i++)
  3. {
  4. Data.Add(i);
  5. }
代码语言:txt
AI代码解释
复制
    下面我们定义4个方法,分别为for,foreach,并行For,并行ForEach。并测试他们的运行时长。

Code

  1. /// <summary>
  2. /// 是否显示执行过程
  3. /// </summary>
  4. public bool ShowProcessExecution = false;
  5. /// <summary>
  6. /// 这是普通循环for
  7. /// </summary>
  8. private void Demo1()
  9. {
  10. List<int> data = Program.Data;
  11. DateTime dt1 = DateTime.Now;
  12. for (int i = 0; i < data.Count; i++)
  13. {
  14. Thread.Sleep(500);
  15. if (ShowProcessExecution)
  16. Console.WriteLine(data[i]);
  17. }
  18. DateTime dt2 = DateTime.Now;
  19. Console.WriteLine("普通循环For运行时长:{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  20. }
  21. /// <summary>
  22. /// 这是普通循环foreach
  23. /// </summary>
  24. private void Demo2()
  25. {
  26. List<int> data = Program.Data;
  27. DateTime dt1 = DateTime.Now;
  28. foreach (var i in data)
  29. {
  30. Thread.Sleep(500);
  31. if (ShowProcessExecution)
  32. Console.WriteLine(i);
  33. }
  34. DateTime dt2 = DateTime.Now;
  35. Console.WriteLine("普通循环For运行时长:{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  36. }
  37. /// <summary>
  38. /// 这是并行计算For
  39. /// </summary>
  40. private void Demo3()
  41. {
  42. List<int> data = Program.Data;
  43. DateTime dt1 = DateTime.Now;
  44. Parallel.For(0, data.Count, (i) =>
  45. {
  46. Thread.Sleep(500);
  47. if (ShowProcessExecution)
  48. Console.WriteLine(data[i]);
  49. });
  50. DateTime dt2 = DateTime.Now;
  51. Console.WriteLine("并行运算For运行时长:{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  52. }
  53. /// <summary>
  54. /// 这是并行计算ForEach
  55. /// </summary>
  56. private void Demo4()
  57. {
  58. List<int> data = Program.Data;
  59. DateTime dt1 = DateTime.Now;
  60. Parallel.ForEach(data, (i) =>
  61. {
  62. Thread.Sleep(500);
  63. if (ShowProcessExecution)
  64. Console.WriteLine(i);
  65. });
  66. DateTime dt2 = DateTime.Now;
  67. Console.WriteLine("并行运算ForEach运行时长:{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  68. }

下面是运行结果:

这里我们可以看出并行循环在执行效率上的优势了。

结论1:在对一个数组内的每一个项做单独处理时,完全可以选择并行循环的方式来提升执行效率。

原理1:并行计算的线程开启是缓步开启的,线程数量1,2,4,8缓步提升。(不详,PLinq最多64个线程,可能这也是64)

二、 并行循环的中断和跳出

代码语言:txt
AI代码解释
复制
    当在进行循环时,偶尔会需要中断循环或跳出循环。下面是两种跳出循环的方法Stop和Break,LoopState是循环状态的参数。

Code

  1. /// <summary>
  2. /// 中断Stop
  3. /// </summary>
  4. private void Demo5()
  5. {
  6. List<int> data = Program.Data;
  7. Parallel.For(0, data.Count, (i, LoopState) =>
  8. {
  9. if (data[i] > 5)
  10. LoopState.Stop();
  11. Thread.Sleep(500);
  12. Console.WriteLine(data[i]);
  13. });
  14. Console.WriteLine("Stop执行结束。");
  15. }
  16. /// <summary>
  17. /// 中断Break
  18. /// </summary>
  19. private void Demo6()
  20. {
  21. List<int> data = Program.Data;
  22. Parallel.ForEach(data, (i, LoopState) =>
  23. {
  24. if (i > 5)
  25. LoopState.Break();
  26. Thread.Sleep(500);
  27. Console.WriteLine(i);
  28. });
  29. Console.WriteLine("Break执行结束。");
  30. }
代码语言:txt
AI代码解释
复制
    执行结果如下:

结论2:使用Stop会立即停止循环,使用Break会执行完毕所有符合条件的项。

三、并行循环中为数组/集合添加项

代码语言:txt
AI代码解释
复制
    上面的应用场景其实并不是非常多见,毕竟只是为了遍历一个数组内的资源,我们更多的时候是为了遍历资源,找到我们所需要的。那么请继续看。

下面是我们一般会想到的写法:

Code

  1. private void Demo7()
  2. {
  3. List<int> data = new List<int>();
  4. Parallel.For(0, Program.Data.Count, (i) =>
  5. {
  6. if (Program.Data[i] % 2 == 0)
  7. data.Add(Program.Data[i]);
  8. });
  9. Console.WriteLine("执行完成For.");
  10. }
  11. private void Demo8()
  12. {
  13. List<int> data = new List<int>();
  14. Parallel.ForEach(Program.Data, (i) =>
  15. {
  16. if (Program.Data[i] % 2 == 0)
  17. data.Add(Program.Data[i]);
  18. });
  19. Console.WriteLine("执行完成ForEach.");
  20. }

看起来应该是没有问题的,但是我们多次运行后会发现,偶尔会出现错误如下:

这是因为List是非线程安全的类,我们需要使用System.Collections.Concurrent命名空间下的类型来用于并行循环体内。

说明

BlockingCollection<T>

为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻止和限制功能。

ConcurrentBag<T>

表示对象的线程安全的无序集合。

ConcurrentDictionary<TKey, TValue>

表示可由多个线程同时访问的键值对的线程安全集合。

ConcurrentQueue<T>

表示线程安全的先进先出 (FIFO) 集合。

ConcurrentStack<T>

表示线程安全的后进先出 (LIFO) 集合。

OrderablePartitioner<TSource>

表示将一个可排序数据源拆分成多个分区的特定方式。

Partitioner

提供针对数组、列表和可枚举项的常见分区策略。

Partitioner<TSource>

表示将一个数据源拆分成多个分区的特定方式。

那么我们上面的代码可以修改为,加了了ConcurrentQueue和ConcurrentStack的最基本的操作。

Code

  1. /// <summary>
  2. /// 并行循环操作集合类,集合内只取5个对象
  3. /// </summary>
  4. private void Demo7()
  5. {
  6. ConcurrentQueue<int> data = new ConcurrentQueue<int>();
  7. Parallel.For(0, Program.Data.Count, (i) =>
  8. {
  9. if (Program.Data[i] % 2 == 0)
  10. data.Enqueue(Program.Data[i]);//将对象加入到队列末尾
  11. });
  12. int R;
  13. while (data.TryDequeue(out R))//返回队列中开始处的对象
  14. {
  15. Console.WriteLine(R);
  16. }
  17. Console.WriteLine("执行完成For.");
  18. }
  19. /// <summary>
  20. /// 并行循环操作集合类
  21. /// </summary>
  22. private void Demo8()
  23. {
  24. ConcurrentStack<int> data = new ConcurrentStack<int>();
  25. Parallel.ForEach(Program.Data, (i) =>
  26. {
  27. if (Program.Data[i] % 2 == 0)
  28. data.Push(Program.Data[i]);//将对象压入栈中
  29. });
  30. int R;
  31. while (data.TryPop(out R))//弹出栈顶对象
  32. {
  33. Console.WriteLine(R);
  34. }
  35. Console.WriteLine("执行完成ForEach.");
  36. }

ok,这里返回一个序列的问题也解决了。

结论3:在并行循环内重复操作的对象,必须要是thread-safe(线程安全)的。集合类的线程安全对象全部在System.Collections.Concurrent命名空间下。

四、返回集合运算结果/含有局部变量的并行循环

代码语言:txt
AI代码解释
复制
    使用循环的时候经常也会用到迭代,那么在并行循环中叫做 含有局部变量的循环 。下面的代码中详细的解释,这里就不啰嗦了。

Code

  1. /// <summary>
  2. /// 具有线程局部变量的For循环
  3. /// </summary>
  4. private void Demo9()
  5. {
  6. List<int> data = Program.Data;
  7. long total = 0;
  8. //这里定义返回值为long类型方便下面各个参数的解释
  9. Parallel.For<long>(0, // For循环的起点
  10. data.Count, // For循环的终点
  11. () => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
  12. (i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前索引,LoopState是循环状态,subtotal为局部变量名
  13. {
  14. subtotal += data[i]; // 修改局部变量
  15. return subtotal; // 传递参数给下一个迭代
  16. },
  17. (finalResult) => Interlocked.Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
  18. );
  19. Console.WriteLine(total);
  20. }
  21. /// <summary>
  22. /// 具有线程局部变量的ForEach循环
  23. /// </summary>
  24. private void Demo10()
  25. {
  26. List<int> data = Program.Data;
  27. long total = 0;
  28. Parallel.ForEach<int, long>(data, // 要循环的集合对象
  29. () => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
  30. (i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前元素,LoopState是循环状态,subtotal为局部变量名
  31. {
  32. subtotal += i; // 修改局部变量
  33. return subtotal; // 传递参数给下一个迭代
  34. },
  35. (finalResult) => Interlocked.Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
  36. );
  37. Console.WriteLine(total);
  38. }

结论4:并行循环中的迭代,确实很伤人。代码太难理解了。

五、PLinq(Linq的并行计算)

代码语言:txt
AI代码解释
复制
       上面介绍完了For和ForEach的并行计算盛宴,微软也没忘记在Linq中加入并行计算。下面介绍Linq中的并行计算。

4.0中在System.Linq命名空间下加入了下面几个新的类:

说明

ParallelEnumerable

提供一组用于查询实现 ParallelQuery{TSource} 的对象的方法。这是 Enumerable 的并行等效项。

ParallelQuery

表示并行序列。

ParallelQuery<TSource>

表示并行序列。

原理2:PLinq最多会开启64个线程

原理3:PLinq会自己判断是否可以进行并行计算,如果不行则会以顺序模式运行。

原理4:PLinq会在昂贵的并行算法或成本较低的顺序算法之间进行选择,默认情况下它选择顺序算法。

在ParallelEnumerable中提供的并行化的方法

ParallelEnumerable 运算符

说明

AsParallel()

PLINQ 的入口点。指定如果可能,应并行化查询的其余部分。

AsSequential()

指定查询的其余部分应像非并行 LINQ 查询一样按顺序运行。

AsOrdered()

指定 PLINQ 应保留查询的其余部分的源序列排序,直到例如通过使用 orderby 子句更改排序为止。

AsUnordered()

指定查询的其余部分的 PLINQ 不需要保留源序列的排序。

WithCancellation()

指定 PLINQ 应定期监视请求取消时提供的取消标记和取消执行的状态。

WithDegreeOfParallelism()

指定 PLINQ 应当用来并行化查询的处理器的最大数目。

WithMergeOptions()

提供有关 PLINQ 应当如何(如果可能)将并行结果合并回到使用线程上的一个序列的提示。

WithExecutionMode()

指定 PLINQ 应当如何并行化查询(即使默认行为是按顺序运行查询)。

ForAll()

多线程枚举方法,与循环访问查询结果不同,它允许在不首先合并回到使用者线程的情况下并行处理结果。

Aggregate() 重载

对于 PLINQ 唯一的重载,它启用对线程本地分区的中间聚合以及一个用于合并所有分区结果的最终聚合函数。

下面是PLinq的简单代码

Code

  1. /// <summary>
  2. /// PLinq简介
  3. /// </summary>
  4. private void Demo11()
  5. {
  6. var source = Enumerable.Range(1, 10000);
  7. //查询结果按source中的顺序排序
  8. var evenNums = from num in source.AsParallel().AsOrdered()
  9. where num % 2 == 0
  10. select num;
  11. //ForAll的使用
  12. ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
  13. var query = from num in source.AsParallel()
  14. where num % 10 == 0
  15. select num;
  16. query.ForAll((e) => concurrentBag.Add(e \* e));
  17. }

上面代码中使用了ForAll,ForAll和foreach的区别如下:

PLinq的东西很繁杂,但是都只是几个简单的方法,熟悉下方法就好了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-01-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
2017年12月编程语言排行榜:Kotlin成最强黑马,C语言再次崛起
TIOBE 快要宣布“2017 年度编程语言”得主,而 Kotlin 和 C 语言目前成为了“2017编程语言”奖项的得主的最有利竞争者。(“年度编程语言”是授予在一年中比率增长量最高的编程语言。) C 语言是世界上最古老的编程语言之一,去年的评分却跌10%以下,并且看似尚未有回升之势。但由于小型软件设备的蓬勃发展,以及低端软件在汽车行业的增长,C 语言在 2017 年收获了不错的流行度。 Kotlin成最强黑马 作为 C 语言的主要竞争对手的 Kotlin 是相对较新的一门语言,它于 2010 年由 J
顶级程序员
2018/04/26
7220
2017年12月编程语言排行榜:Kotlin成最强黑马,C语言再次崛起
2018 年的编程语言发展趋势
在科技驱动的世界,各行各业都在从根本上发展技术,业界领袖更是将其作为公司的重点。而这些技术的核心部分就是编程语言。国外一位技术爱好者 Ben 整理了一份最流行和最具影响力的编程语言清单,可以帮助开发者更好的预测 2018 年的编程语言发展趋势,同时,有针对性地选择和加强编程语言学习。 这份清单的整理主要考虑了 TIOBE,Indeed.com 和 Github 上的数据指标: TIOBE 每月都会更新一次编程语言排行榜,整合世界各地的使用数据,发布最新的流行趋势。 Indeed.com 是世界最大的
企鹅号小编
2018/02/02
1.7K0
2018 年的编程语言发展趋势
Java 薪资不如 Python,JavaScript 最受欢迎,2018 年主流编程语言一较高下!
鉴于现代编程语言的数量非常繁多,所以决定采用哪种语言并掌握它可能是一项艰巨的任务。
CDA数据分析师
2018/12/14
5750
GitHub年度报告:Python首次击败Java,中国成第二大开源贡献国
报告还发现,以“深度学习”、“自然语言处理”和“机器学习”为主题的repo在过去的一年中越发受欢迎。
小小詹同学
2019/11/14
3500
GitHub年度报告:Python首次击败Java,中国成第二大开源贡献国
2020 , 10 大受欢迎的全球顶级编程语言与薪资水平
英文 | https://codeburst.io/10-top-programming-languages-in-2019-for-developers-a2921798d652
开发者技术前线
2020/11/24
6640
2020 , 10 大受欢迎的全球顶级编程语言与薪资水平
Kotlin 一统天下?Kotlin/Native 开始支持 iOS 和 Web 开发
近日,首届官方举办的 Kotlin 专题会议 KotlinConf 正式开幕,来自世界各地约 1200 名与会者齐聚旧金山,在这场盛大的 KotlinConf 大会主题演讲中,Kotlin 的首席语言设计师 Andrey Breslav 公布了一系列与 Kotlin 紧密相关的重大发展成果。接下来,我们不妨关注一下。
4xx.me
2022/06/09
1.7K0
Kotlin 一统天下?Kotlin/Native 开始支持 iOS 和 Web 开发
Java失宠,谷歌宣布Kotlin现在是Android开发的首选语言
谷歌昨天宣布,Kotlin 编程语言现在是 Android 应用程序开发人员的首选语言。
新智元
2019/05/15
1.3K0
Java失宠,谷歌宣布Kotlin现在是Android开发的首选语言
编程语言趋势最新报告:开发者最青睐DevOps,Kotlin增长最快
2019 年 11 月至 2020 年 2 月期间,SlashData 对 159 个国家和地区的 17000 多名开发者进行了调查。报告指出了目前编程语言领域的几点重要趋势:
机器之心
2020/04/28
5820
Kotlin生态调查结果出炉:超过6成的开发者用过Kotlin了Kotlin 开发者社区
Kotlin 整体的发展速度还是让不少开发者为之兴奋。同时鉴于 JetBrains 和Google 的大力支持,以及开发者社区的热捧,我们也相信它会越来越流行。但如今主要的问题是,它在未来是否会成功地入主 Android 以外的应用?
一个会写诗的程序员
2018/12/05
6760
Kotlin生态调查结果出炉:超过6成的开发者用过Kotlin了Kotlin 开发者社区
2020 年编程语言盘点展望:Java 老兵不死,Kotlin 蓄势待发
链接: https://www.oreilly.com/radar/where-programming-languages-are-headed-in-2020/ 作者:Zan McQuade & Amanda Quinn
逆锋起笔
2020/06/01
8670
【玩转腾讯云】2020 年编程语言盘点展望:Java 老兵不死,Kotlin 蓄势待发
在进入新的十年之际,各行各业都在进行盘点与展望。SegmentFault 作为开发者社区与科技行业的垂直媒体,一直关注行业的发展与相关动态,近期已陆续为大家整理了各大平台、社区针对技术领域作出的预测与盘点。
小李子
2020/05/11
1.2K0
【玩转腾讯云】2020 年编程语言盘点展望:Java 老兵不死,Kotlin 蓄势待发
“老而不死”的三种编程语言
导读:在软件世界中,铁打的二进制,流水的语言。从计算机诞生至今,不知诞生了多少门编程语言。译者查了一下 Wikipedia,好家伙,名单上足足有几百种!但并不是所有的语言都能长期占据 Top 10。
IT阅读排行榜
2019/11/20
8750
“老而不死”的三种编程语言
Kotlin和C 成年度编程语言“候选人”
原文:https://www.tiobe.com/tiobe-index/ 编译:开源中国 下个月,TIOBE 就要宣布“2017 年度编程语言”的最佳得主,而 Kotlin 和 C 语言目前似乎成为了此荣誉的最佳候选人。 C 语言是世界上最古老的编程语言之一,近几年,由于小型软件设备的普及,以及低端软件在汽车行业的增长,C 语言在 2017 年收获了不小的流行度。 而作为竞争对手的 Kotlin 则比较新,于 2011 年由 JetBrains 发布,能运行于 Java 虚拟机之上,因成为 Android
企鹅号小编
2018/01/10
8650
Kotlin和C 成年度编程语言“候选人”
2020 年 JVM 生态报告:Kotlin 成为第二受欢迎的 JVM 语言
2020 年 JVM 生态报告已于近日发布,该报告由 Snyk 和 The Java Magazine(Oracle 的双月刊)联合推出,旨在了解 JDK 的实现、工具、平台和应用方面的前景。
会呼吸的Coder
2020/02/17
5980
2020 年开发者生态报告:Python超越Java,Go、Kotlin强势崛起
近日,JetBrains发布了一份2020年开发者生态报告。该报告是基于19696个开发者的反馈所得到的,目标是完成对2020年开发环境,开发工具,开发语言的趋势调研。
新智元
2020/06/17
8660
2020 年开发者生态报告:Python超越Java,Go、Kotlin强势崛起
DeepSeek R1 对编程语言未来的预测
今天分享下 DeepSeek 对于编程语言未来的预测,这个应该也是很多开发者关注的,因为它可能会影响未来的职业选择。
孔令飞
2025/02/26
510
DeepSeek R1 对编程语言未来的预测
2017 年 Java 大事件总览及未来预测
在过去的一年中,Java 历经了许多变化。在今年年初,Java EE 处于一个不确定的状态,Java 9 版本也推迟了它的发布日期。在 2016 年的 JavaOne 上,甲骨文宣布了解决平台的计划和 Java SE 9 和 OpenJDK 9 的相关信息。 2017 年 Java 大事件回顾 一、JCP EC 投票通过 Java 模块化系统 2017年6月,Java Community Process 执行委员会投票通过了被称为 JSR 376 的 Java 平台模块系统,该平台为 Java 9 奠定了基
CSDN技术头条
2018/02/07
6580
2017 年 Java 大事件总览及未来预测
TIOBE 12 月排行榜:古老的 C 和后起之秀 Kotlin,谁是年度编程语言之王?
本文介绍了 TIOBE 12 月编程语言排行榜,并探讨了年度编程语言的最佳候选人。其中,C 语言和 Kotlin 成为了最佳候选者。此外,Python 和 Java 分别位居第三和第四。
企鹅号小编
2017/12/26
9960
TIOBE 12 月排行榜:古老的 C 和后起之秀 Kotlin,谁是年度编程语言之王?
堕落 Java vs 新贵 Python,2018 年最应该学习哪一门编程语言?
本文主要介绍了2017年编程语言的发展状况以及未来趋势。作者通过分析各大编程语言排行榜,总结出目前最受欢迎的编程语言,并分析了各种语言的特点和应用场景。此外,还对量子编程语言和Python的未来趋势进行了介绍和分析。
企鹅号小编
2018/01/04
1.4K0
堕落 Java vs 新贵 Python,2018 年最应该学习哪一门编程语言?
2019 年 11 月编程语言排行榜
TIOBE 11 月份的编程语言排行榜已经公布,官方的标题是: C 语言已经很接近 Java 了,Swift 排名进入了前 10,Rust 排名达到历史新高。
良月柒
2019/12/18
1.2K0
推荐阅读
相关推荐
2017年12月编程语言排行榜:Kotlin成最强黑马,C语言再次崛起
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档