博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#并行和多线程编程_(1)认识Parallel
阅读量:5788 次
发布时间:2019-06-18

本文共 6812 字,大约阅读时间需要 22 分钟。

Parallel: 英 [ˈpærəlel]    美 [ˈpærəˌlɛl] ,并联的,并行的。

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

一、 Parallel的使用

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

1、Parallel.Invoke:最简单,最简洁的将串行的代码并行化。

先介绍一下StopWatch(using System.Diagnostics)

其使用方法如下:

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

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

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

stopWatch.Reset();  //重置StopWatch

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

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

新建一个控制台程序,添加一个类,代码如下:

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循环的功能相似,下面就在类中添加一个方法来测试一下:

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,代码如下:

public void ParallelForMethod()         {            var obj = new Object();            long num = 0;            ConcurrentBag
bag = new ConcurrentBag
(); 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是并行执行的呢?下面来写个测试代码:

Parallel.For(0, 100, i =>            {               Console.Write(i + "\t");            });

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

3、Parallel.Foreach

 这个方法跟Foreach方法很相似。

List
list = new List
(); list.Add(0); Parallel.ForEach(list, item => { //DoWork(item); });

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

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

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

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

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

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

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

public void ParallelBreak()         {            ConcurrentBag
bag = new ConcurrentBag
(); 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的代码,修改后代码如下:

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循环遍历输出异常信息,可以看到两个异常信息都显示了。

转载于:https://www.cnblogs.com/yaosuc/p/4570993.html

你可能感兴趣的文章
centos 修改字符界面分辨率
查看>>
LNMP之Mysql主从复制(四)
查看>>
阅读Spring源代码(1)
查看>>
nagios一键安装脚本,nagios监控被监控主机上的应用服务mysql数据库
查看>>
grep 命令
查看>>
JS二维数组的声明和使用
查看>>
v$archive_gap dg dataguard 断档处理 scn恢复
查看>>
问责IT风险管理:CIO需关注两个重点
查看>>
Winform打包发布图解
查看>>
PDF文件怎么编辑,超简单的方法
查看>>
EasyUI基础入门之Easyloader(载入器)
查看>>
Uva 839 Not so Mobile
查看>>
30款超酷的HTTP 404页面未找到错误设计
查看>>
程序猿必备 MyEclipse2013-2014系列
查看>>
java中ArrayList 、LinkList区别
查看>>
Spring ’14 Wave Update: Installing Dynamics CRM on Tablets for Windows 8.1
查看>>
利用rand7()构造rand10()
查看>>
MySQL 备份与恢复
查看>>
吃午饭前,按书上的代码写会儿--Hunt the Wumpus第一个版本
查看>>
easyui中combobox的值改变onchang事件
查看>>