.NET4中加入了并行机制——所谓并行就是同时开辟若干个线程来进行计算。这些线程由于都是互相独立的,所以在进行一些分布式(比如各自不同的工作)的时候是非常简单,不过要把这些处理结果汇总起来却不是那么容易——下面来看一个非常简单的例子(求1~1000的和)。

如果你尝试使用以下的代码计算,恐怕令你大跌眼镜!

[C#]

int sum = 0;
Parallel.For(0, 1000,i => {sum+=i;});

[VB.NET]

Dim sum As Integer = 0
Parallel.[For](0, 1000, Function(i)
sum += i)

究其原因就是.NET会默认开辟一些线程同时进行“sum+=i”的计算。那么由于sum被这些线程同时使用,往往是一个线程还没有处理完毕,另外一个线程又介入了,自然无法得到正确结果了。

解决这个问题的办法有许多:

【一】分解法:

所谓分解法,就是针对“同一个变量”被不同线程“共享”这一诟病而提出的。——也就是说,把1~1000求和分成若干块进行处理(等于给每一个线程分配了不同的内存)。最后把分布计算的结果进行累计汇总即可。结果如下:

[C#]

int[] numbers = Enumerable.Range(1, 1000).ToArray();
int[] values=new int[4];
int sum = 0;
Parallel.For(0, 4, i => { values[i] = new Program().GetTotal(i * 250, 250, numbers); });
sum = values.Sum();
Console.WriteLine(sum);

[VB.NET]

Dim numbers As Integer() = Enumerable.Range(1, 1000).ToArray()
Dim values As Integer() = New Integer(3) {}
Dim sum As Integer = 0
Parallel.[For](0, 4, Function(i)
values(i) = New Program().GetTotal(i * 250, 250, numbers))
sum = values.Sum()
Console.WriteLine(sum)

【二】使用lock(锁住一个变量,然后直到该线程操作完毕自动释放变量,另外一个线程进来操作……如此反复而已):

[C#]

 public class Program{int sum = 0;

private static object obj = new object();

            public void ShowResult()
            {Parallel.For(1, 1001, i => { lock (obj) { sum += i; Thread.Sleep(10); } });Console.WriteLine(sum);}static void Main(string[] args){Program p = new Program();p.ShowResult();}}

[VB.NET]

Public Class ProgramPrivate sum As Integer = 0Private Shared obj As New ObjectPublic Sub ShowResult()Dim obj As New Object()Parallel.For(1, 1001, Sub(i)SyncLock objsum = sum + iEnd SyncLockEnd Sub)Console.WriteLine(sum)End Sub
End ClassModule MSub Main()Dim p As New Programp.ShowResult()End Sub
End Module

【三】使用InternLock函数:

[C#]

 public class Program{int sum = 0;public void ShowResult(){Parallel.For(1, 1001, i => { Interlocked.Add(ref sum, i); });Console.WriteLine(sum);}static void Main(string[] args){Program p = new Program();p.ShowResult();}}

[VB.NET]

Public Class ProgramPrivate sum As Integer = 0Public Sub ShowResult()Parallel.[For](1, 1001, Sub(i)Interlocked.Add(sum, i)End Sub)Console.WriteLine(sum)End SubShared Sub Main(args As String())Dim p As New Program()p.ShowResult()End Sub
End Class

一个更复杂的例子在于多线程“并行”遍历某个文件夹中全部的文件,并且添加到DataTable中:

[C#]

public class Program{/// <summary> /// Each time process with 3 files /// </summary> public const int BLOCKFILEPROCESS = 3;private static object flag = new object();static void Main(string[] args){DataTable dt = new DataTable();dt.Columns.Add("Id", typeof(int));dt.Columns.Add("FileName", typeof(string));dt.Columns.Add("ExtensionName", typeof(string));dt.Columns[0].AutoIncrement = true;dt.Columns[0].AutoIncrementSeed = 1;dt.Columns[0].AutoIncrementStep = 1;string[] files = Directory.GetFiles("c:\\文件夹名称", "*.*", SearchOption.AllDirectories);int totalFiles = files.Length;int finalThreadNum = totalFiles / BLOCKFILEPROCESS == 0 ? (totalFiles / BLOCKFILEPROCESS) : (totalFiles / BLOCKFILEPROCESS + 1);Parallel.For<string[]>(0, finalThreadNum, () => files, (i, state, processCollection) =>{return processCollection.Skip(i * BLOCKFILEPROCESS).Take(BLOCKFILEPROCESS).ToArray();},(result) =>{Monitor.Enter(flag);{foreach (var item in result){DataRow row = dt.NewRow();row["FileName"] = Path.GetFileName(item);row["ExtensionName"] = Path.GetExtension(item);dt.Rows.Add(row);}Monitor.Exit(flag);}});foreach (DataRow item in dt.Rows){Console.WriteLine(item["Id"].ToString() + "<==>" + item["FileName"] + "<==>" + item["ExtensionName"].ToString());}}}

[VB.NET]

Public Class Program''' <summary> ''' Each time process with 3 files ''' </summary> Public Const BLOCKFILEPROCESS As Integer = 3Private Shared flag As New Object()Private Shared Sub Main(args As String())Dim dt As New DataTable()dt.Columns.Add("Id", GetType(Integer))dt.Columns.Add("FileName", GetType(String))dt.Columns.Add("ExtensionName", GetType(String))dt.Columns(0).AutoIncrement = Truedt.Columns(0).AutoIncrementSeed = 1dt.Columns(0).AutoIncrementStep = 1Dim files As String() = Directory.GetFiles("c:\文件夹名称", "*.*", SearchOption.AllDirectories)Dim totalFiles As Integer = files.LengthDim finalThreadNum As Integer = If(totalFiles \ BLOCKFILEPROCESS = 0, (totalFiles \ BLOCKFILEPROCESS), (totalFiles \ BLOCKFILEPROCESS + 1))Parallel.[For](Of String())(0, finalThreadNum, Function() files, Function(i, state, processCollection) Return processCollection.Skip(i * BLOCKFILEPROCESS).Take(BLOCKFILEPROCESS).ToArray()End Function, Function(result) Monitor.Enter(flag)If True ThenFor Each item As var In resultDim row As DataRow = dt.NewRow()row("FileName") = Path.GetFileName(item)row("ExtensionName") = Path.GetExtension(item)dt.Rows.Add(row)NextMonitor.[Exit](flag)End IfEnd Function)For Each item As DataRow In dt.RowsConsole.WriteLine((item("Id").ToString() & "<==>") + item("FileName") & "<==>" & item("ExtensionName").ToString())NextEnd Sub
End Class

上面的示例代码使用了Monitor进行对多线程访问同一个对象的锁定和解锁(其实lock的本质是调用了Monitor,不过lock花费的时间比Monitor要长)。如果不用lock或者Monitor锁定,那么将造成多线程访问最有一个DataTable,造成竞争现象的发生。相对第一个For的非泛型示例而言,最大区别在于第一个非泛型的For解决方案1在求和时是为每一个线程单独分配了存储单元,然而这个示例的存储单元(DataTable)却是共享的。

一般地,如果多线程共享一个资源,对一个资源操作,必须采用“同步”机制来控制!

另外,这里的For使用了其泛型的版本:第一,第二个参数决定循环的次数(其实是分块多少块,准备开多少线程;第三个参数每次会返回一个IEnumerable集合供特性的线程操作,第四个参数委托将自定义返回给当前已经创建线程的集合(示例中是使用Skip+Take方法计算返回给当前线程多少数据)。最后一个委托是无参数的Action,直接对返回的集合进行处理(添加进入表格中)。

同样这里还可以使用Paralle.ForEach,注意ForEach无法分块(因为内部已经分块的,具体开多少线程无法人为控制),代码参考如下:

[C#]

 public class Program{/// <summary> /// Each time process with 3 files /// </summary> public const int BLOCKFILEPROCESS = 3;private static object flag = new object();static void Main(string[] args){DataTable dt = new DataTable();dt.Columns.Add("Id", typeof(int));dt.Columns.Add("FileName", typeof(string));dt.Columns.Add("ExtensionName", typeof(string));dt.Columns[0].AutoIncrement = true;dt.Columns[0].AutoIncrementSeed = 1;dt.Columns[0].AutoIncrementStep = 1;string[] files = Directory.GetFiles("c:\\安装", "*.jpg", SearchOption.TopDirectoryOnly);Parallel.ForEach<string>(files, (s) => {lock (flag){DataRow row = dt.NewRow();row["FileName"] = Path.GetFileName(s);row["ExtensionName"] = Path.GetExtension(s);dt.Rows.Add(row);}});foreach (DataRow item in dt.Rows){Console.WriteLine(item["Id"].ToString() + "<==>" + item["FileName"] + "<==>" + item["ExtensionName"].ToString());}}}

[VB.NET]

Public Class Program''' <summary> ''' Each time process with 3 files ''' </summary> Public Const BLOCKFILEPROCESS As Integer = 3Private Shared flag As New Object()Private Shared Sub Main(args As String())Dim dt As New DataTable()dt.Columns.Add("Id", GetType(Integer))dt.Columns.Add("FileName", GetType(String))dt.Columns.Add("ExtensionName", GetType(String))dt.Columns(0).AutoIncrement = Truedt.Columns(0).AutoIncrementSeed = 1dt.Columns(0).AutoIncrementStep = 1Dim files As String() = Directory.GetFiles("c:\安装", "*.jpg", SearchOption.TopDirectoryOnly)Parallel.ForEach(Of String)(files, Function(s) SyncLock flagDim row As DataRow = dt.NewRow()row("FileName") = Path.GetFileName(s)row("ExtensionName") = Path.GetExtension(s)dt.Rows.Add(row)End SyncLockEnd Function)For Each item As DataRow In dt.RowsConsole.WriteLine((item("Id").ToString() & "<==>") + item("FileName") & "<==>" & item("ExtensionName").ToString())NextEnd Sub
End Class

转载于:https://www.cnblogs.com/ServiceboyNew/archive/2012/05/12/2497340.html

关于Paralle.For和Paralle.ForEach相关推荐

  1. Java面试题-JVM 和服务器性能评估

    1.JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots 垃圾指的是内存中不再使用的空间(主要指的就是堆内存),确定垃圾的方法有引用记数法和可达性分析法(但引用计数法存在对象之间循环引用的 ...

  2. 你知道的各种形状(CSS实现各种形状)

    转载于:https://juejin.cn/post/6900785241233817608 单纯通过CSS我们就可以实现页面的形状样式.CSS绘制的形状随着屏幕分辨率的提高不会出现变形失真的问题,值 ...

  3. #pragma omp paralle与#pragma omp parallel for

    今天写OpenMP的程序,遇到很让人恼火的问题,三个#pragma omp paralle没有问题, 再有一个#pragma omp paralle,计算结果就错误了. 修改调试了一个晚上,快要崩溃了 ...

  4. Scala paralle

    list.par def par: scala.collection.parallel.immutable.ParSeq[Int] val list = List(1,2,3,4,5) list.pa ...

  5. Resistors in Paralle题解

    假设这个数是n,质因数分解后可以写成     n=a1^k1*a2^k2*--*ai^ki     所求的数的因数和f(n)就等于     f(n)=(1+a1+a1^2+--+a1^k1)*(1+a ...

  6. 浅显易懂 Makefile 入门 (07)— 其它函数(foreach 、if、call、origin )

    1. foreach 函数 foreach 函数定义如下: $(foreach <var>,<list>,<text>) 函数的功能是:把参数 <list&g ...

  7. 2021年大数据常用语言Scala(二十一):函数式编程 遍历 foreach

    目录 遍历  foreach 使用类型推断简化函数定义 使用下划线来简化函数定义 遍历  foreach 之前,学习过了使用for表达式来遍历集合.我们接下来将学习scala的函数式编程,使用fore ...

  8. [JS] for-each和map()的区别

    先说下for-each与for-of还有for-in的区别. for-of和for-in都是对对象操作的,而for-each和map()是对数组进行操作的. for-in的使用:对于一个数组里的每一个 ...

  9. java 增强for循环(foreach)

    学而时习之,温故而知新. java的增强fou循环(foreach) 是java5的新特征之一 foreach的循环对象一般是一个集合,List.ArrayList.LinkedList.Vector ...

最新文章

  1. 【面试虐菜】—— Oracle知识整理《收获,不止Oracle》
  2. poj 2411 Mondriaan#39;s Dream 【dp】
  3. python读取文件报错必须有一个正确的读写方式_python文件操作
  4. 我们找阿里云资深技术专家李响聊了聊开源和云原生
  5. 我的win7黑屏解决方案
  6. 代码款空题 包的使用
  7. OnlineDict:Chrome取词翻译扩展
  8. Python处理僵尸进程
  9. 为树莓派制作系统镜像时进行瘦身,方便后续保存与批量写入
  10. 军事训练飞机的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  11. 小程序底部导航栏动态生成
  12. 大使、布道师、贡献者,OpenHarmony社区发起三大贡献者激励计划
  13. jeecg中高级查询的写法
  14. 转载-杭电老师的思考
  15. java毕业设计校园社区系统mybatis+源码+调试部署+系统+数据库+lw
  16. 如何把excel里面中文小写数字转换成阿拉伯数字
  17. 一些学校的ACM网址
  18. 通过数组指针给二维数组赋值
  19. 神经网络中的反向传播
  20. 360 css grid,CSS Grid 網格佈局教程

热门文章

  1. Unity3D 优化相关
  2. 1月全球Web服务器市场:Apache份额回升至41.64%
  3. JavaScript中的 apply 与 call 方法
  4. PHP中exec、system等函数调用linux命令问题
  5. php软件开发--公众平台
  6. bagging算法_Bagging/Boosting傻傻分不清?来一探究竟吧~
  7. devops .net_DevOps vs. Agile:它们有什么共同点吗?
  8. 自动化脚本上传图片怎么办_一切都自动化后我们将怎么办?
  9. 温度传感器硬件编号_打开硬件传感器BITalino进行酷项目
  10. linux安全 4a标准_Linux的未来,提高安全性的开放标准等等