c# 并行和多线程编程——认识Parallel-程序员宅基地

技术标签: C#  多线程  并行  

  随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能。在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Tasks。这里面有很多关于并行开发的东西,今天第一篇就介绍下最基础,最简单的——认识和使用Parallel。

一、 Parallel的使用

在Parallel下面有三个常用的方法invoke,For和ForEach。

1、Parallel.Invoke

这是最简单,最简洁的将串行的代码并行化。

在这里先讲一个知识点,就是StopWatch的使用,最近有一些人说找不到StopWatch,StopWatch到底是什么东西,今天就来说明一下。

StopWatch在System.Diagnostics命名控件,要使用它就要先引用这个命名空间。

其使用方法如下:

1

2

3

4

5

6

7

8

9

10

11

var stopWatch = new StopWatch();   //创建一个Stopwatch实例

stopWatch.Start();   //开始计时

stopWatch.Stop();   //停止计时

stopWatch.Reset();  //重置StopWatch

stopWatch.Restart(); //重新启动被停止的StopWatch

stopWatch.ElapsedMilliseconds //获取stopWatch从开始到现在的时间差,单位是毫秒

本次用到的就这么多知识点,想了解更多关于StopWatch的,去百度一下吧,网上有很多资料。

下面进入整体,开始介绍Parallel.Invoke方法,废话不多说了,首先新建一个控制台程序,添加一个类,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

public class ParallelDemo

 {

 private Stopwatch stopWatch = new Stopwatch();

 public void Run1()

 {

 Thread.Sleep(2000);

 Console.WriteLine("Task 1 is cost 2 sec");

 }

 public void Run2()

 {

 Thread.Sleep(3000);

 Console.WriteLine("Task 2 is cost 3 sec");

 }

 public void ParallelInvokeMethod()

 {

 stopWatch.Start();

 Parallel.Invoke(Run1, Run2);

 stopWatch.Stop();

 Console.WriteLine("Parallel run " + stopWatch.ElapsedMilliseconds + " ms.");

  

 stopWatch.Restart();

 Run1();

 Run2();

 stopWatch.Stop();

 Console.WriteLine("Normal run " + stopWatch.ElapsedMilliseconds + " ms.");

 }

代码很简单,首先新加一个类,在类中写了两个方法,Run1和Run2,分别等待一定时间,输出一条信息,然后写了一个测试方法ParallelInvokeMethod,分别用两种方法调用Run1和Run2,然后在main方法中调用,下面来看一下运行时间如何:

  大家应该能够猜到,正常调用的话应该是5秒多,而Parallel.Invoke方法调用用了只有3秒,也就是耗时最长的那个方法,可以看出方法是并行执行的,执行效率提高了很多。

2、Parallel.For

这个方法和For循环的功能相似,下面就在类中添加一个方法来测试一下吧。代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public void ParallelForMethod()

 {

 stopWatch.Start();

 for (int i = 0; i < 10000; i++)

 {

 for (int j = 0; j < 60000; j++)

 {

  int sum = 0;

  sum += i;

 }

 }

 stopWatch.Stop();

 Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms.");

 stopWatch.Reset();

 stopWatch.Start();

 Parallel.For(0, 10000, item =>

 {

 for (int j = 0; j < 60000; j++)

 {

  int sum = 0;

  sum += item;

 }

 });

 stopWatch.Stop();

 Console.WriteLine("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");

  

 }

写了两个循环,做了一些没有意义的事情,目的主要是为了消耗CPU时间,同理在main方法中调用,运行结果如下图:

可以看到,Parallel.For所用的时间比单纯的for快了1秒多,可见提升的性能是非常可观的。那么,是不是Parallel.For在任何时候都比for要快呢?答案当然是“不是”,要不然微软还留着for干嘛?

下面修改一下代码,添加一个全局变量num,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

public void ParallelForMethod()

 {

 var obj = new Object();

 long num = 0;

 ConcurrentBag<long> bag = new ConcurrentBag<long>();

 stopWatch.Start();

 for (int i = 0; i < 10000; i++)

 {

 for (int j = 0; j < 60000; j++)

 {

  //int sum = 0;

  //sum += item;

  num++;

 }

 }

 stopWatch.Stop();

 Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms.");

 stopWatch.Reset();

 stopWatch.Start();

 Parallel.For(0, 10000, item =>

 {

 for (int j = 0; j < 60000; j++)

 {

  //int sum = 0;

  //sum += item;

  lock (obj)

  {

  num++;

  }

 }

 });

 stopWatch.Stop();

 Console.WriteLine("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");

  

 }

Parallel.For由于是并行运行的,所以会同时访问全局变量num,为了得到正确的结果,要使用lock,此时来看看运行结果:

  是不是大吃一惊啊?Parallel.For竟然用了15秒多,而for跟之前的差不多。这主要是由于并行同时访问全局变量,会出现资源争夺,大多数时间消耗在了资源等待上面。

一直说并行,那么从哪里可以看出来Parallel.For是并行执行的呢?下面来写个测试代码:

1

2

3

4

Parallel.For(0, 100, i =>

 {

 Console.Write(i + "\t");

 });

从0输出到99,运行后会发现输出的顺序不对,用for顺序肯定是对的,并行同时执行,所以会出现输出顺序不同的情况。

3、Parallel.Foreach

这个方法跟Foreach方法很相似,想具体了解的,可以百度些资料看看,这里就不多说了,下面给出其使用方法:

1

2

3

4

5

6

List<int> list = new List<int>();

 list.Add(0);

 Parallel.ForEach(list, item =>

 {

 DoWork(item);

 });

二、 Parallel中途退出循环和异常处理

1、当我们使用到Parallel,必然是处理一些比较耗时的操作,当然也很耗CPU和内存,如果我们中途向停止,怎么办呢?

在串行代码中我们break一下就搞定了,但是并行就不是这么简单了,不过没关系,在并行循环的委托参数中提供了一个ParallelLoopState,

该实例提供了Break和Stop方法来帮我们实现。

Break: 当然这个是通知并行计算尽快的退出循环,比如并行计算正在迭代100,那么break后程序还会迭代所有小于100的。

Stop:这个就不一样了,比如正在迭代100突然遇到stop,那它啥也不管了,直接退出。

下面来写一段代码测试一下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public void ParallelBreak()

 {

 ConcurrentBag<int> bag = new ConcurrentBag<int>();

 stopWatch.Start();

 Parallel.For(0, 1000, (i, state) =>

 {

 if (bag.Count == 300)

 {

  state.Stop();

  return;

 }

 bag.Add(i);

 });

 stopWatch.Stop();

 Console.WriteLine("Bag count is " + bag.Count + ", " + stopWatch.ElapsedMilliseconds);

 }

这里使用的是Stop,当数量达到300个时,会立刻停止;可以看到结果"Bag count is 300",如果用break,可能结果是300多个或者300个,大家可以测试一下。

2、异常处理

  首先任务是并行计算的,处理过程中可能会产生n多的异常,那么如何来获取到这些异常呢?普通的Exception并不能获取到异常,然而为并行诞生的AggregateExcepation就可以获取到一组异常。

这里我们修改Parallel.Invoke的代码,修改后代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

public class ParallelDemo

 {

 private Stopwatch stopWatch = new Stopwatch();

 public void Run1()

 {

 Thread.Sleep(2000);

 Console.WriteLine("Task 1 is cost 2 sec");

 throw new Exception("Exception in task 1");

 }

 public void Run2()

 {

 Thread.Sleep(3000);

 Console.WriteLine("Task 2 is cost 3 sec");

 throw new Exception("Exception in task 2");

 }

 public void ParallelInvokeMethod()

 {

 stopWatch.Start();

 try

 {

 Parallel.Invoke(Run1, Run2);

 }

 catch (AggregateException aex)

 {

 foreach (var ex in aex.InnerExceptions)

 {

  Console.WriteLine(ex.Message);

 }

 }

 stopWatch.Stop();

 Console.WriteLine("Parallel run " + stopWatch.ElapsedMilliseconds + " ms.");

 stopWatch.Reset();

 stopWatch.Start();

 try

 {

 Run1();

 Run2();

 }

 catch(Exception ex)

 {

 Console.WriteLine(ex.Message);

 }

 stopWatch.Stop();

 Console.WriteLine("Normal run " + stopWatch.ElapsedMilliseconds + " ms.");

 }

顺序调用方法我把异常处理写一起了,这样只能捕获Run1的异常信息,大家可以分开写。捕获AggregateException 异常后,用foreach循环遍历输出异常信息,可以看到两个异常信息都显示了。

点击这里,下载源码

以上就是c# 并行和多线程编程——认识Parallel的详细内容,更多关于c# 并行和多线程编程的资料请关注脚本之家其它相关文章!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/biyusr/article/details/122554435

智能推荐

顺丰对供应链+区块链应用的思考与规划_顺丰 区块链 成本-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏6次。顺丰科技大数据负责人陈新江以“顺丰对供应链+区块链应用的思考与规划”为题进行了主题分享。分享主要分为三个部分:一是顺丰对供应链的思考;二是顺丰供应链+区块链的规划;三是数字智能供应链蓝图。以下是分享的主要内容。顺丰,大家应该比较熟悉,顺丰作为一家物流供应链企业,在进行数字化转型时所需要的科技能力和知识储备,都是由顺丰科技来承载和实现的。顺丰科技于2009年成立,致力于成为独立第三方行业解决方案的数据科技服务公司,聚焦实现物流大网和供应链底盘的数智化转型与升级,通过打通营运、销售、体验等环节与板块的数字._顺丰 区块链 成本

org.springframework.dao.InvalidDataAccessApiUsageException-程序员宅基地

文章浏览阅读5.2w次,点赞11次,收藏16次。在配置 springmvc+hibernate+mysql 的时候,出现如下问题:异常信息:org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session i_org.springframework.dao.invaliddataaccessapiusageexception

引擎开发二: stb_image库及使用_stb_image.h下载-程序员宅基地

文章浏览阅读1w次,点赞10次,收藏24次。  stb_image 是一个简单易用的图像解码库。安装及使用环境:win7 VS20151. 下载stb_image :github地址:https://github.com/nothings/stb2. opengl项目配置:  因为stb_image库实现都写在头文件中,不需要编译成库,项目中直接引用头文件目录即可。a. 项目属性 ----> C/C++ —> 附..._stb_image.h下载

前后端分离,我怎么就选择了 Spring Boot + Vue 技术栈?_为什么选择springboot+vue对比其他技术的优势-程序员宅基地

文章浏览阅读1.4w次,点赞36次,收藏117次。前两天又有小伙伴私信松哥,问题还是职业规划,Java 技术栈路线这种,实际上对于这一类问题我经常不太敢回答,每个人的情况都不太一样,而小伙伴也很少详细介绍自己的情况,大都是一两句话就把问题抛出来了,啥情况都不了解,就要指出一个方向,这实在是太难了。因此今天我想从我学习 Spring Boot + Vue 这套技术栈的角度,来和大家聊一聊没有人指导,我是如何一步一步建立起自己的技术体系的。线上大..._为什么选择springboot+vue对比其他技术的优势

Failed to execute goal com.spotify:docker-maven-plugin:1.2.2:build (build-image) : Exception caught-程序员宅基地

文章浏览阅读5.5k次,点赞3次,收藏4次。有一个可能的原因是因为docker 没有开启2375远程访问docker功能Docker 安装成功之后,我们首先需要修改 Docker 配置,开启允许远程访问 Docker 的功能,开启方式很简单,修改 /usr/lib/systemd/system/docker.service 文件,加入如下内容:-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock配置完成后,保存退出,然后重启 Docker:systemctl daemon-reloads_failed to execute goal com.spotify:docker-maven-plugin:1.2.2:build (build-im

rapter求n的阶乘流程图_流程图编程-教学课件.docx-程序员宅基地

文章浏览阅读6.4k次。流程图编程:1、请按如下要求完成Raptor流程图编程: (1)编写Raptor3437.rap实现:从键盘输入任意整数N,当N≤0时显示“Error!”,否则利用循环结构计算并显示1 + 3 + 5 + … + (2×N - 1)的值。 (2)交卷:单击此处上传文件“Raptor3437.rap”到服务器。允许上传:Raptor3437.rap2、请按如下要求完成Raptor流程图编..._raptor求n的阶乘

随便推点

Java程序员面试一览:java基础,JVM,并发,锁,网络、数据库-程序员宅基地

文章浏览阅读531次,点赞21次,收藏27次。可能有人会问我为什么愿意去花时间帮助大家实现求职梦想,因为我一直坚信时间是可以复制的。我牺牲了自己的大概十个小时写了这片文章,换来的是成千上万的求职者节约几天甚至几周时间浪费在无用的资源上。上面的这些(算法与数据结构)+(Java多线程学习手册)+(计算机网络顶级教程)等学习资源《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取![外链图片转存中…(img-VFHCffex-1713497287013)]

aop执行顺序_around before 顺序-程序员宅基地

文章浏览阅读1k次。顺序:around,before先执行,然后拦截链上的执行完后,执行目标方法,最后根据执行结果来执行throwing,returning,一定会执行after.注意around和before顺序不是固定的,根据拓扑排序来确定。啥也不做,回到拦截链上,如果有异常,然后不会异常,又抛出异常。先去执行before里面的方法然后回到拦截链上。此时拦截链已经执行完了,然后就去执行目标方法。回到拦截链,最后一定会执行,finally。又回到拦截链上,然后获取到返回值。第二行又回到拦截链上了。啥也不做,回到拦截链上。_around before 顺序

基于微信群控系统分析几十万几百万用户微信朋友圈和聊天记录数据-程序员宅基地

文章浏览阅读244次。基于微信群控系统分析几十万几百万用户朋友圈和聊天记录数据打造针对用户的智能推荐系统用户属性:姓名、性别、年龄、所在地区、常驻地区、手机号码、微信号码、职业、岗位、身份证等等用户行为:1、通过图文分析,定位所在区域、行业、大概的收入状况、喜好;2、如果是微商,分析常发微信圈产品;3、综合分析朋友圈人气状况;4、给用户打标签;5、产品匹配。建立用户画像标签和大数据分析..._微信群控、数据分析

python idle 输入多行_python实现在IDLE中输入多行-程序员宅基地

文章浏览阅读273次。{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里技术人对外发布原创技术内容的最大平台;社区覆盖了云计算、大数据、人工智能、IoT、云原生、数据库、微服务、安全、开发与运维9大技术领域。","link1":..._python idle 输入多行

react native 可伸缩、拖动、放大缩小、关闭bounding box_reactnative实现拖拽文字可以放大缩小-程序员宅基地

文章浏览阅读824次。稍后会开源到github上,这里先上代码:import React, { Component } from 'react';import { Dimensions, View, TouchableWithoutFeedback, PanResponder} from 'react-native';import PropTypes from 'prop-types';import { Connector, CONNECTOR_TOP_LEFT,_reactnative实现拖拽文字可以放大缩小

【npm】npm私有库的使用-绑定_.npmrc配置私有仓库-程序员宅基地

文章浏览阅读571次。若要在专门发包的项目中,发包到自己的私有库,需要在项目文件夹中创建一个。文件建好后,当前文件夹下的npm库已经独立出来。可以直接在项目目录下输入。全局绑定了其他的私有库。_.npmrc配置私有仓库

推荐文章

热门文章

相关标签