从第一台计算机问世到现在计算机硬件技术已经有了很大的发展。不管是现在个人使用的PC还是公司使用的服务器。双核,四核,八核的CPU已经非常常见。这样我们可以将我们程序分摊到多个计算机CPU中去计算,在过去并行化需要线程的低级操作,难度很大,在.net4.0中的增强了对并行化的支持,使得这一切变得非常简单 。本次我从以下几个方面大家讲以下.NET 并行
- 数据并行
- 任务并行
- 并行Linq
- 任务工厂
- 注意事项
本次主要给大家讲一下数据并行 废话不说,下面开始了
数据并行其实就是指对原集合或者数组中的数据进行划区之后分摊到多个CPU或者多个线程执行相同的操作 在 .net中的 System.Threading.Tasks 提供了对数据并行的支持类 , Parallel.For,Parallel.ForEach和我们经常的使用的for 和foreach 十分的相似,你不用创建线程队列,在基本的循环中你不用使用锁。这些.net 会帮你处理,你只需要关注你自己的业务 那下面我们就来看看 Parallel.For 和 Parallel.ForEach 是如何使用的
- Parallel.For 简单使用
Parallel.For(0, 100, i => { dosameting() });
上面的例子是不是和我们经常使用的for循环的影子。 说一下 Parallel.For 的第三个参数Action<int>类型的委托 不管这个委托的参数是0个还是多少个他的返回植都是void,那么怎么样才能获取到Parallel.For 中的的返回值了,下面的例子将演示如何使用线程本地变量来存储和检索由 for 循环创建的每个单独任务中的状态 通过使用线程本地数据,您可以避免将大量的访问同步为共享状态的开销。 在任务的所有迭代完成之前,您将计算和存储值,而不是写入每个迭代上的共享资源。 然后,您可以将最终结果一次性写入共享资源,或将其传递到另一个方法
- 对个list<int> 进行求和我们这里假设List的长度为listLength
Parallel.For<long>(0, listLength, () => 0, (j, loop, subsum) => { subsum += lista[j]; return subsum; }, (x) => Interlocked.Add(ref sum, x));
- 在现实中我们也经常会遇到需要取消循环的情况。比如你在队列中查找一个数。那么如何退出Parallel.For 循环了。是不是也是和for 和foreach那样 使用Break关键字就可以了,答案否定的。这是因为break 构造对循环是有效的,而并行循环其实是一个方法,并不是循环 那么要怎么样取消了。请看下面的例子
Parallel.For<long>(0, listLength, () => 0, (j, loop, subsum) => { if (subsum > 20000) { loop.Break(); } subsum += lista[j]; return subsum; }, (x) => Interlocked.Add(ref sum, x));
- 简单Parallel.ForEach 循环 Parallel.ForEach循环的工作方式类似于 Parallel.For 循环 根据系统环境,对源集合进行分区,并在多个线程上计划工作。 系统中的处理器越多,并行方法的运行速度越快。 对于某些源集合,顺序循环可能更快,具体取决于源的大小和正在执行的工作类型
Parallel.ForEach(lista, i => { dosameting(); });
不知道大家在这个地方有没有看到foreach的影子。其实上面的例子中的ForEach方法的最后一个输入参数是 Action<int>委托,当所有循环完成时,方法将调用该委托。这个地方和前面的Parallel.For 是一样的。那么我们要如何获得返回值了和上面的For是非常相似,我依然以上面数组求和为例
1
2
3
4
5
6
7
8
9
10
11
|
Parallel.ForEach< int , long >(lista, () => 0, (j, loop, subsum) => { if (subsum > 20000) { loop.Break(); } subsum += lista[j]; return subsum; }, (x) => Interlocked.Add( ref sum, x)); |
- Parallel.For 和for 性能测试比较 我们这里产生1千万个随机数为例子做个一个性能比较,在笔者的笔记本上结果如下 (可能在你的电脑上得到结果不一定相同)
附上相关的代码给大家参考
int listLength = 10000000; List<int> listTask = new List<int>(); List<int> list = new List<int>(); Stopwatch watch1 = Stopwatch.StartNew(); Parallel.For(0, listLength, i => { Random r = new Random(100); listTask.Add(r.Next()); }); Console.WriteLine("并行耗时:" + watch1.ElapsedMilliseconds ); Stopwatch watch2 = Stopwatch.StartNew(); for (int i = 0; i < listLength; i++) { Random r = new Random(100); list.Add(r.Next()); } Console.WriteLine("非并行耗时:" + watch2.ElapsedMilliseconds );
这里谢谢冰麟轻武 希望上面的例子对大家理解并行计算有帮助
欢迎大家阅读 .net并行运行之任务并行
软件工程之系统建模篇【设计实体类模型】
本文主要介绍实体类模型的设计过程,首先识别类及类之间的关系,然后画出类图和包图,最后识别类的属性和操作。类是面向对象方法的一个全新概念,类模型是面向对象分析的核心,实体类位于系统结构的商业规则服务层。实体类是系统需要持久保存的对象最终要映射到数据库。实体类模型用类图和包图描述。
1、类的识别
1.1 类的识别
识别类币识别用例要困难的多,实体世界中,一切都是对象,识别起来并非易事。我们在程序设计过程中,一般是用名词识别方法,然而你也可以用其他的方法。用名词识别法时,从系统中找出名词、名词短语或名词性代词,他们往往对应着类。其中单数名词可以识别为对象,而复数名词则可以识别为类。并不是每个名词都对应着一个对象或类,可能有的名词是其他对象的一个属性,也可能几个名词对应着一个对象或类。
1.2 识别过程示例
第一步,从系统描述中找出描述问题域实体的名词。如:草拟人、草搞文件、办理人、公文、机关领导、办理意见、审核意见、复核人、签发人、分发人、发文登记表等
第二步,从候选对象或类中筛选去掉一部分名词。如:公文比较笼统,可以去掉。办理人、复核人、签发人、分发人等可以识别为一个类“用户(User)”。
2、类的关联
要建立类模型,不仅要识别出类,还要识别出类与类之间的关系。显示的关系可以从用例中找到,如:用户(User)涉及会议参加人(Attendee),而隐式的关系在用例中没有明确说明,这需要项目分析员进一步去分析。
3、类图
通过以上步骤可以识别出系统的类以及类之间的关系,现在可以画出类图。先画出单个类图,分析每一个类与其他类的关系,再画出系统类图,从而建立实体类模型。
4、包图
在结构化方法中,划分子系统最常用的方法是功能分解。与此相对应,UML则提供了按用例类实现对问题域的分解,并用包图来管理模型元素。包是一种分组机制,它将UML模型元素中紧密相关的部分放置在同一个包中,实行集中管理,以减小系统的规模。
5、类属性
类的属性一般用于描述类的特征,在识别属性时,要从类的语义完整性的角度来斟酌取舍。所谓语义完整性,是指类的属性能够在一起完整地描述一个类所具有的特性和特征。从系统找出的名词,并不都对应着一个对象或类,有的名词可能是其他对象的一个属性。类的属性最后映射到数据库中的数据表的列,与类的识别一样,类属性的识别也需要往复多次才能完成。
与用户相关的信息中,本系统只关心姓名、部门、职务、用户唯一标识、访问权限、密码对应的属性分别为name、department、occupation、account、accessAuthority、password。
6、类操作
类的操作定义了类支持的行为。如用户(User)类应该具有登录操作,草拟公文等你操作。
基于python的《Hadoop权威指南》一书中气象数据下载和map reduce化数据处理及其可视化
文档内容:
1:下载《hadoop权威指南》中的气象数据
2:对下载的气象数据归档整理并读取数据
3:对气象数据进行map reduce进行处理
关键词:《Hadoop权威指南》气象数据 map reduce python matplotlib可视化
一:下载《hadoop权威指南》一书中的气象数据
《hadoop权威指南》一书中的气象数据位于 http://ftp3.ncdc.noaa.gov/pub/data/noaa/,
新建 getdata.py文件, 并加入如下代码:
运行getdata.py,将在当前目录下生成数据文件
二:对下载的气象数据归档整理并读取数据
说明:上一步骤在当前目录下生成【1901】~【1921】 共20文件,文件里是压缩的气象数据,本步骤知识将数据移动data文件夹下
新建 movedata.py文件, 并加入如下代码:
三:对气象数据进行map reduce进行处理
说明:这里要读取文件中的数据内容,并通过将数据map reduce 化获取每年的最高、低温度
1: 将文件中的数据内容逐行读出
新建reader.py文件,并加入如下代码:
测试上面代码:在命令行运行 reader.py,查看输出
2:通过mapper方法把数据处理成 "year temperature"的输出形式,如 "1901 242",其中 242 表示温度为24.2度
新建mapper.py文件,并加入如下代码:
测试上面代码:在命令行运行 reader.py | mapper.py ,查看输出。(注:这是是利用管道,把reader.py的输出作为mapper.py的输入)
3:通过reducer方法将mapper的输出数据整理并计算每年的最高、低温度,并输出
新建reducer.py文件,并加入如下代码:
测试上面代码:在命令行运行 reader.py | mapper.py | reducer.py,查看输出。
4:使用matplotlib对每年的最高、低数据进行可视化
新建drawer.py文件,并加入如下代码:
测试上面代码:在命令行运行 reader.py | mapper.py | reducer.py | drawer.py,查看输出。
显示效果如下图:(注:在前面处理的数据中, 可能由于采样的错误,会有出现999.9度的最高温度, 显然不符常理。在本例中,没有对此种错误进行深究,一致将超度50度的温度处理成50度)
四 说明
1. 本例中,其实第二步 对下载的气象数据归档整理并读取数据 是多余的, 可以直接在第一步中修改文件存储目录跳过第二步。但为了熟悉python对文件的操作,还是将第二步的代码保留了下来。
2. 本例中,代码能运行得到实验目标,但并为对代码进行优化。请读者根据需要自行更改。
3. python代码的一大特点就是看起来像伪代码,又本例python代码比较简单,故没有给出注释。
算法:五步教你消除递归
目录
背景函数的调用过程使用堆中分配的栈消除递归递归版本非递归版本消除过程第一步:识别递归版本中的代码地址第二步:定义栈帧第三步:while 循环第四步:switch 语句。第五步:填充 case 代码体。汉诺塔练习二叉树遍历练习备注背景返回目录
递归对于分析问题比较有优势,但是基于递归的实现效率就不高了,而且因为函数栈大小的限制,递归的层次也有限制。本文给出一种通用的消除递归的步骤,这样您可以在分析阶段采用递归思想,而实现阶段采用非递归算法。
函数的调用过程返回目录
函数的调用是基于栈,每次调用都涉及如下操作:
- 调用开始时:将返回地址和局部变量入栈。
- 调用结束时:出栈并将返回到入栈时的返回地址。
使用堆中分配的栈消除递归返回目录
递归版本返回目录
代码
1 public static int Triangle(int n) 2 { 3 // 地址:2 4 if (n == 1) 5 { 6 // 地址:4 7 return n; 8 } 9 10 /* 11 * 地址:4 地址:3 12 * / / 13 * / / 14 * / / */ 15 return n + Triangle(n - 1); 16 }
非递归版本返回目录
代码
1 private class StackFrame 2 { 3 public int N; 4 public int ReturnAddress; 5 } 6 7 public static int Triangle2(int n) 8 { 9 var stack = new Stack<StackFrame>(); 10 var currentReturnValue = 0; 11 var currentAddress = 1; 12 13 while (true) 14 { 15 switch (currentAddress) 16 { 17 case 1: 18 { 19 stack.Push(new StackFrame 20 { 21 N = n, 22 ReturnAddress = 5 23 }); 24 currentAddress = 2; 25 } 26 break; 27 case 2: 28 { 29 var frame = stack.Peek(); 30 if (frame.N == 1) 31 { 32 currentReturnValue = 1; 33 currentAddress = 4; 34 } 35 else 36 { 37 stack.Push(new StackFrame 38 { 39 N = frame.N - 1, 40 ReturnAddress = 3 41 }); 42 currentAddress = 2; 43 } 44 } 45 break; 46 case 3: 47 { 48 var frame = stack.Peek(); 49 currentReturnValue = frame.N + currentReturnValue; 50 currentAddress = 4; 51 } 52 break; 53 case 4: 54 { 55 currentAddress = stack.Pop().ReturnAddress; 56 } 57 break; 58 case 5: 59 { 60 return currentReturnValue; 61 } 62 } 63 }
消除过程返回目录
第一步:识别递归版本中的代码地址返回目录
- 第一个代表:原始方法调用。
- 倒数第一个代表:原始方法调用结束。
- 第二个代表:方法调用入口。
- 倒数第二个代表:方法调用出口。
- 递归版本中的每个递归调用定义一个代码地址。
假如递归调用了 n 次,则代码地址为:n + 4。
1 public static int Triangle(int n) 2 { 3 // 地址:2 4 if (n == 1) 5 { 6 // 地址:4 7 return n; 8 } 9 10 /* 11 * 地址:4 地址:3 12 * / / 13 * / / 14 * / / */ 15 return n + Triangle(n - 1); 16 }
第二步:定义栈帧返回目录
栈帧代表了代码执行的上下文,将递归版本代码体中用到的局部值类型变量定义为栈帧的成员变量,为啥引用类型不用我就不多说了,另外还需要定义一个返回地址成员变量。
1 private class StackFrame 2 { 3 public int N; 4 public int ReturnAddress; 5 }
第三步:while 循环返回目录
在 while 循环之前声明一个 stack、一个 currentReturnValue 和 currentAddress。
1 public static int Triangle2(int n) 2 { 3 var stack = new Stack<StackFrame>(); 4 var currentReturnValue = 0; 5 var currentAddress = 1; 6 7 while (true) 8 { 9 } 10 }
第四步:switch 语句。返回目录
1 public static int Triangle2(int n) 2 { 3 var stack = new Stack<StackFrame>(); 4 var currentReturnValue = 0; 5 var currentAddress = 1; 6 7 while (true) 8 { 9 switch (currentAddress) 10 { 11 case 1: 12 { 13 } 14 break; 15 case 2: 16 { 17 } 18 break; 19 case 3: 20 { 21 } 22 break; 23 case 4: 24 { 25 } 26 break; 27 case 5: 28 { 29 } 30 } 31 } 32 }
第五步:填充 case 代码体。返回目录
将递归版本的代码做如下变换:
- 函数调用使用:stack.push(new StackFrame{...}); 和 currentAddress = 2; 。
- 引用的局部变量变为,比如:n,变为:stack.Peek().n 。
- return 语句变为:currentReturnValue = 1; 和 currentAddress = 4; 。
- 倒数第一个 case 代码体为:return currentReturnValue; 。
最终的效果就是上面的示例。
汉诺塔练习返回目录
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataStuctureStudy.Recursives 8 { 9 class HanoiTest 10 { 11 public static void Hanoi(int n, string source, string middle, string target) 12 { 13 if (n == 1) 14 { 15 Console.WriteLine(String.Format("{0}->{1}", source, target)); 16 } 17 else 18 { 19 Hanoi(n - 1, source, target, middle); 20 Console.WriteLine(String.Format("{0}->{1}", source, target)); 21 Hanoi(n - 1, middle, source, target); 22 } 23 } 24 25 public static void Hanoi2(int n, string source, string middle, string target) 26 { 27 var stack = new Stack<StackFrame>(); 28 var currentAddress = 1; 29 30 while (true) 31 { 32 switch (currentAddress) 33 { 34 case 1: 35 { 36 stack.Push(new StackFrame 37 { 38 N = n, 39 Source = source, 40 Middle = middle, 41 Target = target, 42 ReturnAddress = 5 43 }); 44 currentAddress = 2; 45 } 46 break; 47 case 2: 48 { 49 var frame = stack.Peek(); 50 if (frame.N == 1) 51 { 52 Console.WriteLine(String.Format("{0}->{1}", frame.Source, frame.Target)); 53 currentAddress = 4; 54 } 55 else 56 { 57 stack.Push(new StackFrame 58 { 59 N = frame.N - 1, 60 Source = frame.Source, 61 Middle = frame.Target, 62 Target = frame.Middle, 63 ReturnAddress = 3 64 }); 65 currentAddress = 2; 66 } 67 } 68 break; 69 case 3: 70 { 71 var frame = stack.Peek(); 72 Console.WriteLine(String.Format("{0}->{1}", frame.Source, frame.Target)); 73 stack.Push(new StackFrame 74 { 75 N = frame.N - 1, 76 Source = frame.Middle, 77 Middle = frame.Source, 78 Target = frame.Target, 79 ReturnAddress = 4 80 }); 81 currentAddress = 2; 82 } 83 break; 84 case 4: 85 currentAddress = stack.Pop().ReturnAddress; 86 break; 87 case 5: 88 return; 89 } 90 } 91 } 92 93 private class StackFrame 94 { 95 public int N; 96 public string Source; 97 public string Middle; 98 public string Target; 99 public int ReturnAddress; 100 } 101 } 102 }
二叉树遍历练习返回目录
这个练习是我之前采用的方式看,思想和上面的非常相似。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataStuctureStudy.Recursives 8 { 9 class TreeTest 10 { 11 public static void Test() 12 { 13 RecursiveTraverse(Node.BuildTree()); 14 StackTraverse(Node.BuildTree()); 15 } 16 17 private class Node 18 { 19 public Node Left { get; set; } 20 21 public Node Right { get; set; } 22 23 public int Value { get; set; } 24 25 public static Node BuildTree() 26 { 27 return new Node 28 { 29 Value = 1, 30 Left = new Node 31 { 32 Value = 2, 33 Left = new Node 34 { 35 Value = 3 36 }, 37 Right = new Node 38 { 39 Value = 4 40 } 41 }, 42 Right = new Node 43 { 44 Value = 5, 45 Left = new Node 46 { 47 Value = 6 48 }, 49 Right = new Node 50 { 51 Value = 7 52 } 53 } 54 }; 55 } 56 } 57 58 private static void RecursiveTraverse(Node node) 59 { 60 if (node == null) 61 { 62 return; 63 } 64 65 RecursiveTraverse(node.Left); 66 Console.WriteLine(node.Value); 67 RecursiveTraverse(node.Right); 68 } 69 70 private enum CodeAddress 71 { 72 Start, 73 AfterFirstRecursiveCall, 74 AfterSecondRecursiveCall 75 } 76 77 private class StackFrame 78 { 79 public Node Node { get; set; } 80 81 public CodeAddress CodeAddress { get; set; } 82 } 83 84 private static void StackTraverse(Node node) 85 { 86 var stack = new Stack<StackFrame>(); 87 stack.Push(new StackFrame 88 { 89 Node = node, 90 CodeAddress = CodeAddress.Start 91 }); 92 93 while (stack.Count > 0) 94 { 95 var current = stack.Peek(); 96 97 switch (current.CodeAddress) 98 { 99 case CodeAddress.Start: 100 if (current.Node == null) 101 { 102 stack.Pop(); 103 } 104 else 105 { 106 current.CodeAddress = CodeAddress.AfterFirstRecursiveCall; 107 stack.Push(new StackFrame 108 { 109 Node = current.Node.Left, 110 CodeAddress = CodeAddress.Start 111 }); 112 } 113 break; 114 case CodeAddress.AfterFirstRecursiveCall: 115 Console.WriteLine(current.Node.Value); 116 117 current.CodeAddress = CodeAddress.AfterSecondRecursiveCall; 118 stack.Push(new StackFrame 119 { 120 Node = current.Node.Right, 121 CodeAddress = CodeAddress.Start 122 }); 123 break; 124 case CodeAddress.AfterSecondRecursiveCall: 125 stack.Pop(); 126 break; 127 } 128 } 129 } 130 } 131 }
备注返回目录
搞企业应用的应该用不到这种消除递归的算法,不过学完以后对递归的理解也更清晰了。