5天玩转C#并行和多线程编程系列文章目录

5天玩转C#并行和多线程编程 —— 第一天 认识Parallel

5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

5天玩转C#并行和多线程编程 —— 第四天 Task进阶

5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结

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

  一、 Parallel的使用

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

1、  Parallel.Invoke

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

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

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

其使用方法如下:

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

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

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

stopWatch.Reset();  //重置StopWatch

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

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

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

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

 public class ParallelDemo
{
private Stopwatch stopWatch = new Stopwatch(); public void Run1()
{
Thread.Sleep();
Console.WriteLine("Task 1 is cost 2 sec");
}
public void Run2()
{
Thread.Sleep();
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 = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
int sum = ;
sum += i;
}
}
stopWatch.Stop();
Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms."); stopWatch.Reset();
stopWatch.Start();
Parallel.For(, , item =>
{
for (int j = ; j < ; j++)
{
int sum = ;
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 = ;
ConcurrentBag<long> bag = new ConcurrentBag<long>(); stopWatch.Start();
for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
//int sum = 0;
//sum += item;
num++;
}
}
stopWatch.Stop();
Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms."); stopWatch.Reset();
stopWatch.Start();
Parallel.For(, , item =>
{
for (int j = ; j < ; 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(, , i =>
{
Console.Write(i + "\t");
});

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

2、Parallel.Foreach

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

List<int> list = new List<int>();
list.Add();
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<int> bag = new ConcurrentBag<int>();
stopWatch.Start();
Parallel.For(, , (i, state) =>
{
if (bag.Count == )
{
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();
Console.WriteLine("Task 1 is cost 2 sec");
throw new Exception("Exception in task 1");
}
public void Run2()
{
Thread.Sleep();
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循环遍历输出异常信息,可以看到两个异常信息都显示了。

点击这里,下载源码

5天玩转C#并行和多线程编程 —— 第一天 认识Parallel的更多相关文章

  1. 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 转载 https://www.cnblogs.com/yunfeifei/p/3993401.html

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  2. 5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  3. 5天玩转C#并行和多线程编程 —— 第四天 Task进阶

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  4. 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  5. 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  6. 5天玩转C#并行和多线程编程

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  7. C#并行和多线程编程_(1)认识Parallel

    Parallel: 英 [ˈpærəlel]    美 [ˈpærəˌlɛl] ,并联的,并行的. 随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性 ...

  8. C#并行和多线程编程

    5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq   5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线 ...

  9. 转载 Net多线程编程—System.Threading.Tasks.Parallel

    .Net多线程编程—System.Threading.Tasks.Parallel   System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Paral ...

随机推荐

  1. oracle for loop循环以及游标循环

    1. for in loop形式 DECLARE    CURSOR c_sal IS SELECT employee_id, first_name || last_name ename, salar ...

  2. vSphere6提示已弃用VMFS卷的解决方法

    Deprecated VMFS volume(s) found on the host. Please consider upgrading volume(s) to the latest versi ...

  3. openSUSE 配置静态上网

    openSUSE静态固定IP设置方法 openSUSE号称最华丽的Linux,只是中文资料实在太少,这里提供在VMmware Workstation 7.12中安装openSUSE 11.3设置静态固 ...

  4. 按Enter键触发事件

    1.document.onkeydown=function(e){        var keycode=document.all?event.keyCode:e.which;        if(k ...

  5. js常用的地址栏参数获取

    用JS获取地址栏参数的方法(超级简单) 方法一:采用正则表达式获取地址栏参数:( 强烈推荐,既实用又方便!) function GetQueryString(name) {      var reg ...

  6. OD使用教程9

    先运行程序打开about,发现这是一个未注册的软件需要注册后才能使用里面的工具,所以随便注册一下,跳出一个提示说注册的是非法的邮箱,所以就可以以此做为突破口来破解这个程序 将提示的语句作为关键字去找出 ...

  7. Python小练习二

    # 以正确的宽度在居中的"盒子"内打印一个句子 # 注意,整数除法运算符(//)只能用在Python 2.2及后续版本,在之前的版本中,只使用普通除法(/) sentence = ...

  8. java四种内部类

    内部类有成员内部类,局部内部类,匿名内部类,静态内部类. 1,成员内部类package innerClass; public class InnerClassTest { String s1=&quo ...

  9. 【洛谷P2889】Milking Time

    很容易想到以结束时间加上R从小到大排序 之后怎样呢? 我们按层考虑,f[i]表示前i个时间段嫩得到的最大价值 每次枚举其之前的状态,如果其ed<当前i的st,那么取max即可 #include& ...

  10. vc中获取磁盘IO统计计数

    想知道进程读写磁盘的情况,可以获取当前进程或指定进程的IO计数. #include <Windows.h> int get_io_bytes(ULONGLONG * read_bytes, ...