原文:8天玩转并行开发——第一天 Parallel的使用

随着多核时代的到来,并行开发越来越展示出它的强大威力,像我们这样的码农再也不用过多的关注底层线程的实现和手工控制,

要了解并行开发,需要先了解下两个概念:“硬件线程”和“软件线程”。

1. 硬件线程

相信大家手头的电脑都是双核以上的,像我这样古董的电脑都是双核的,这样的双核叫做物理内核。

硬件线程又叫做逻辑内核,我们可以在”任务管理器“中查看”性能“标签页,如下图,我们知道有2个硬件线程。

一般情况下,一个物理内核对应一个逻辑内核,比如我这里的2对2。当然如果你的cpu采用的是超线程技术,那么可能就会有4个物理内核对应

8个硬件线程,现在有很多服务器都有8个硬件线程,上午在公司的服务器上截了个图。

我们要知道并行开发要做的事情就是将任务分摊给这些硬件线程去并行执行来达到负载和加速。

2. 软件线程

相信这个大家最熟悉了,我们知道传统的代码都是串行的,就一个主线程,当我们为了实现加速而开了很多工作线程,这些工作线程

也就是软件线程。

好,我们知道了基本概念就ok了,在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Tasks。这里面有很多好玩

的东西,作为第一篇就介绍下最基础,最简单的Parallel的使用。

一: Parallel的使用

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

1:  Parallel.Invoke

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

 1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var watch = Stopwatch.StartNew();
6
7 watch.Start();
8
9 Run1();
10
11 Run2();
12
13 Console.WriteLine("我是串行开发,总共耗时:{0}\n", watch.ElapsedMilliseconds);
14
15 watch.Restart();
16
17 Parallel.Invoke(Run1, Run2);
18
19 watch.Stop();
20
21 Console.WriteLine("我是并行开发,总共耗时:{0}", watch.ElapsedMilliseconds);
22
23 Console.Read();
24 }
25
26 static void Run1()
27 {
28 Console.WriteLine("我是任务一,我跑了3s");
29 Thread.Sleep(3000);
30 }
31
32 static void Run2()
33 {
34 Console.WriteLine("我是任务二,我跑了5s");
35 Thread.Sleep(5000);
36 }
37 }

在这个例子中可以获取二点信息:

第一:一个任务是可以分解成多个任务,采用分而治之的思想。

第二:尽可能的避免子任务之间的依赖性,因为子任务是并行执行,所以就没有谁一定在前,谁一定在后的规定了。

2:Parallel.for

我们知道串行代码中也有一个for,但是那个for并没有用到多核,而Paraller.for它会在底层根据硬件线程的运行状况来充分的使用所有的可

利用的硬件线程,注意这里的Parallel.for的步行是1。

这里我们来演示一下,向一个线程安全的集合插入数据,当然这个集合采用原子性来实现线程同步,比那些重量级的锁机制更加的节省消耗。

 1  class Program
2 {
3 static void Main(string[] args)
4 {
5 for (int j = 1; j < 4; j++)
6 {
7 Console.WriteLine("\n第{0}次比较", j);
8
9 ConcurrentBag<int> bag = new ConcurrentBag<int>();
10
11 var watch = Stopwatch.StartNew();
12
13 watch.Start();
14
15 for (int i = 0; i < 20000000; i++)
16 {
17 bag.Add(i);
18 }
19
20 Console.WriteLine("串行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);
21
22 GC.Collect();
23
24 bag = new ConcurrentBag<int>();
25
26 watch = Stopwatch.StartNew();
27
28 watch.Start();
29
30 Parallel.For(0, 20000000, i =>
31 {
32 bag.Add(i);
33 });
34
35 Console.WriteLine("并行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);
36
37 GC.Collect();
38
39 }
40 }
41 }

可以看的出,加速的效果还是比较明显的。

3:Parallel.forEach
    forEach的独到之处就是可以将数据进行分区,每一个小区内实现串行计算,分区采用Partitioner.Create实现。

 class Program
{
static void Main(string[] args)
{
for (int j = 1; j < 4; j++)
{
Console.WriteLine("\n第{0}次比较", j); ConcurrentBag<int> bag = new ConcurrentBag<int>(); var watch = Stopwatch.StartNew(); watch.Start(); for (int i = 0; i < 3000000; i++)
{
bag.Add(i);
} Console.WriteLine("串行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds); GC.Collect(); bag = new ConcurrentBag<int>(); watch = Stopwatch.StartNew(); watch.Start(); Parallel.ForEach(Partitioner.Create(0, 3000000), i =>
{
for (int m = i.Item1; m < i.Item2; m++)
{
bag.Add(m);
}
}); Console.WriteLine("并行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds); GC.Collect(); }
}
}

这里还是要说一下:Partitioner.Create(0, 3000000)。

第一:我们要分区的范围是0-3000000。

第二:我们肯定想知道系统给我们分了几个区? 很遗憾,这是系统内部协调的,无权告诉我们,当然系统也不反对我们自己指定分区个数,

这里可以使用Partitioner.Create的第六个重载,比如这样:Partitioner.Create(0, 3000000, Environment.ProcessorCount),

因为 Environment.ProcessorCount能够获取到当前的硬件线程数,所以这里也就开了2个区。

下面分享下并行计算中我们可能有的疑惑?

<1> 如何中途退出并行循环?

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

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

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

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

下面举个例子,当迭代到1000的时候退出循环

 1   class Program
2 {
3 static void Main(string[] args)
4 {
5 var watch = Stopwatch.StartNew();
6
7 watch.Start();
8
9 ConcurrentBag<int> bag = new ConcurrentBag<int>();
10
11 Parallel.For(0, 20000000, (i, state) =>
12 {
13 if (bag.Count == 1000)
14 {
15 state.Break();
16 return;
17 }
18 bag.Add(i);
19 });
20
21 Console.WriteLine("当前集合有{0}个元素。", bag.Count);
22
23 }
24 }

<2> 并行计算中抛出异常怎么处理?

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

class Program
{
static void Main(string[] args)
{
try
{
Parallel.Invoke(Run1, Run2);
}
catch (AggregateException ex)
{
foreach (var single in ex.InnerExceptions)
{
Console.WriteLine(single.Message);
}
} Console.Read();
} static void Run1()
{
Thread.Sleep(3000);
throw new Exception("我是任务1抛出的异常");
} static void Run2()
{
Thread.Sleep(5000); throw new Exception("我是任务2抛出的异常");
}
}

<3> 并行计算中我可以留一个硬件线程出来吗?

默认的情况下,底层机制会尽可能多的使用硬件线程,然而我们使用手动指定的好处是我们可以在2,4,8个硬件线程的情况下来进行测量加速比。

 class Program
{
static void Main(string[] args)
{
var bag = new ConcurrentBag<int>(); ParallelOptions options = new ParallelOptions(); //指定使用的硬件线程数为1
options.MaxDegreeOfParallelism = 1; Parallel.For(0, 300000, options, i =>
{
bag.Add(i);
}); Console.WriteLine("并行计算:集合有:{0}", bag.Count); }
}

并行开发 1.Parallel的更多相关文章

  1. C# 并行开发总结

    本文内容 均参考自 <C#并行高级编程> TPL 支持 数据并行(有大量数据要处理,必须对每个数据执行同样的操作, 任务并行(有好多可以并发运行的操作),流水线(任务并行和数据并行的结合体 ...

  2. 并行开发——Parallel的使用 -摘自网络

    随着多核时代的到来,并行开发越来越展示出它的强大威力,像我们这样的码农再也不用过多的关注底层线程的实现和手工控制, 要了解并行开发,需要先了解下两个概念:“硬件线程”和“软件线程”. 1. 硬件线程 ...

  3. C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel

    大家好,本次讨论的是C#中的并行开发,给力吧,随着并行的概念深入,哥也赶上这个潮流了,其实之前讨论C#的异步调用或者C#中BeginInvoke或者Invoke都已经涉及了部分本篇的内容. 参考书目: ...

  4. 8天玩转并行开发——第一天 Parallel的使用

    转自:http://www.cnblogs.com/huangxincheng/archive/2012/04/02/2429543.html 随着多核时代的到来,并行开发越来越展示出它的强大威力,像 ...

  5. .NET下的并行开发(案例代码)

    以下主要是通过一个报表处理程序来说明并行开发的方式.对于数据冲突和共享,可以通过对象数组解决.设计到并行的核心代码已用红色标出.在并行程序的处理上,需要把原来串行的子公司变成一个一个类的对象,让所有的 ...

  6. 8天玩转并行开发——第三天 plinq的使用

    原文 8天玩转并行开发——第三天 plinq的使用 相信在.net平台下,我们都玩过linq,是的,linq让我们的程序简洁优美,简直玩的是爱不释手,但是传统的linq只是串行代码,在并行的 年代如果 ...

  7. 并行开发-Paraller

    并行开发的概念 并行开发要做的事情就是将任务分摊给硬件线程去并行执行来达到负载和加速,传统的代码都是串行的,就一个主线程,当我们为了实现加速而开了很多工作线程,这些工作线程就是软件线程 Paralle ...

  8. NET中并行开发优化

    NET中并行开发优化 让我们考虑一个简单的编程挑战:对大数组中的所有元素求和.现在可以通过使用并行性来轻松优化这一点,特别是对于具有数千或数百万个元素的巨大阵列,还有理由认为,并行处理时间应该与常规时 ...

  9. [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)

    [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...

随机推荐

  1. linux基础知识(三)

    添加用户 •useradd -d 家目录 -g 组名 -G 组集合(逗号间隔) -p 密码 -s 用户shell程序 用户名 •useradd -d /usr/local/nginx/ -g ngin ...

  2. hdu 4082 Hou Yi's secret(暴力枚举)

    Hou Yi's secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  3. golang API

    1.server端程序 package main //简单的JSON Restful API演示(服务端) //author: Xiong Chuan Liang //date: 2015-2-28 ...

  4. Mysqldump导入数据库很慢的解决办法

    https://blog.csdn.net/xizaihui/article/details/53103049 1.MySQLdump导出的SQL语句在导入到其他数据库的时候会相当慢,甚至几十秒才处理 ...

  5. Task10.Bert

    Transformer原理 论文地址:Attention Is All You Need:https://arxiv.org/abs/1706.03762 Transformer是一种完全基于Atte ...

  6. python实现Restful服务 (基于flask)(1)

    参考:https://www.jianshu.com/p/6ac1cab17929 参考:https://www.cnblogs.com/alexyuyu/p/6243362.html 参考:http ...

  7. datatables屏蔽警告弹窗

    //不显示任何错误信息 $.fn.dataTable.ext.errMode = 'none'; //以下为发生错误时的事件处理,如不处理,可不管. $('#tableId').on( 'error. ...

  8. 转Oracle、MySql、SQLServer 数据分页查询

    最近简单的对oracle,mysql,sqlserver2005的数据分页查询作了研究,把各自的查询的语句贴出来供大家学习..... (一). mysql的分页查询 mysql的分页查询是最简单的,借 ...

  9. codeforces_D. Treasure Hunting_[DP+Binary Search]

    http://codeforces.com/contest/1201/problem/D 题意:n行m列的矩阵中,有k个targets,从[1, 1]出发,每次只能向上下左右四个方向移动一步,且只有在 ...

  10. C#在WinForm开发中Label换行方法

    很多朋友都会在开发WinForm中遇到Label要显示的内容太长,但却不能换行的问题.这里我总结了几种方法,供大家参考. 第一种是把Label的AutoSize属性设为False,手动修改Label的 ...