一起谈.NET技术,.Net4.0 Parallel编程(二)Data Parallelism 中

简介:   在上篇文章中看过了使用Parrallel.For、Parael.Foreach在效率上给我们带来的提高。本文就来如何终止循环、线程局部变量 进行说明。  Thread-Local Variables  首先我们来看下线程局部变量,是的我们也许一直在想我们如何去定义一个线程局部变量呢。

  在上篇文章中看过了使用Parrallel.For、Parael.Foreach在效率上给我们带来的提高。本文就来如何终止循环、线程局部变量 进行说明。

  Thread-Local Variables

  首先我们来看下线程局部变量,是的我们也许一直在想我们如何去定义一个线程局部变量呢。先看段顺序执行的代码:

 
 
[TestMethod()]
public void NormalSequenceTest()
{
int [] nums = Enumerable.Range( 0 , 1000000 ).ToArray();
long total = 0 ;
for ( int i = 0 ; i < nums.Length;i ++ )
{
total
+= nums[i];
}
Console.WriteLine(
" The total is {0} " , total);
}

  执行结果:

image

  我们再来看这段代码:

 
 
[TestMethod()]
public void NormalParallelTest()
{
int [] nums = Enumerable.Range( 0 , 1000000 ).ToArray();
long total = 0 ;
Parallel.For(
0 ,nums.Length,i =>
{
total
+= nums[i];
});
Console.WriteLine(
" The total is {0} " , total);
}

  执行结果:

image

  再运行下:

image

  也许我们会感到很奇怪为什么会这样呢,其实我们想想就可以明白了,total变量是公共的,而我们的程序是多个线程的加,而多个线程之间是不能把数据共享的。其实我们需要的是在每个线程中计算出一个和值,然后再进行累加。我们来看看线程局部变量:

 
 
[TestMethod()]
public void ThreadLocalTest()
{
int [] nums = Enumerable.Range( 0 , 1000000 ).ToArray();
long total = 0 ;

Parallel.For
< long > ( 0 , nums.Length, () => 0 , (j, loop, subtotal) =>
{
subtotal
+= nums[j];
return subtotal;
},
(x)
=> Interlocked.Add( ref total, x)
);

Console.WriteLine(
" The total is {0} " , total);
}

  我们再看下执行结果:

 image

  下面说下泛型方法Parallel.For<T>方法,方法的原型:

 
 
public static ParallelLoopResult For < TLocal > ( int fromInclusive, int toExclusive, Func < TLocal > localInit, Func < int , ParallelLoopState, TLocal, TLocal > body, Action < TLocal > localFinally);

  TLocal:线程变量的类型;第一个、第二个参数就不必多说了,就是其实值跟结束值。

  localInit:每个线程的线程局部变量初始值的设置;

  body:每次循环执行的方法,其中方法的最后一个参数就是线程局部变量;

  localFinally:每个线程之后执行的方法。

  相信这样解释后就能明白了为什么需要线程局部变量了,也明白如何使用线程局部变量了。我们再看看在Parallel.Foreach<T>中如何使用:

 
 
[TestMethod()]
public void ForeachThreadLocalTest()
{
int [] nums = Enumerable.Range( 0 , 1000000 ).ToArray();
long total = 0 ;
Parallel.ForEach
< int , long > (nums,() => 0 ,(member,loopState,subTotal) =>
{
subTotal
+= member;
return subTotal;
},
(perLocal)
=> Interlocked.Add( ref total,perLocal)
);
Console.WriteLine(
" The total is {0} " , total);
}

  要注意的是,我们必须要使用ForEach<TSource, TLocal>,因为第一个参数表示的是迭代源的类型,第二个表示的是线程局部变量的类型,其方法的参数跟For是差不多的。

  Break、Stop

  首先我们可以看到在Parallel.For的一个重载方法中:

 
 
public static ParallelLoopResult For( int fromInclusive, int toExclusive, Action < int , ParallelLoopState > body);

  在委托的最后一个参数类型为ParallelLoopState,而ParallelLoopState里面提供给我们两个方法:Break、Stop来终止迭代,而Break跟Stop的区别是什么呢?我们来看两段代码:

 
 
private void StopLoop()
{
var Stack
= new ConcurrentStack < string > ();
Parallel.For(
0 , 10000 , (i, loopState) =>
{
if (i < 1000 )
Stack.Push(i.ToString());
else
{
loopState.Stop();
return ;
}
});
Console.WriteLine(
" Stop Loop Info:\n elements count:{0} " , Stack.Count);
}

private void BreakLoop()
{
var Stack
= new ConcurrentStack < string > ();
var stringList
= this .ConstructString( 10000 );
Parallel.For(
0 , stringList.Count, (i, loopState) =>
{
Stack.Push(stringList[i]);
if (stringList[i].Contains( " 999 " ))
{
loopState.Break();
}
});
Console.WriteLine(
" Stop Loop Info:\n elements count:{0} " , Stack.Count);
}

private List < string > ConstructString( int number)
{
var stringList
= new List < string > ();
Parallel.For(
0 , number - 1 , i =>
{
stringList.Add(i.ToString());
});
return stringList;
}

  测试方法:

 
 
[TestMethod()]
public void LoopTest()
{
StopLoop();
BreakLoop();
}

  来看看运行结果吧:

image

  其实这个例子只是想告诉大家,为什么第一个用Stop,第二个用Break。原因是:第一个例子中我只关心的是循环的迭代变量i的值,我只要1000个字符串,而不去管这1000个字符串是什么东西。所以当达到1000时,我们就立刻停止所有的迭代包括其他线程上的。而第二个方法中我们是判断的源中的某个索引值,这个时候有可能较早的元素还未处理。

  其实在我们调用过Stop或者Break方法后,循环上的其他的线程可能还会运行一段时间,其实我们可以通过IsStopped属性来判断循环是在其他线程上停止。Foreach中的使用就不再看了,跟For是一样的。

  总结

  在本文中,主要介绍了如何停止循环、使用线程局部变量。在里面我们看到了我们在使用并行开发时,有很多东西是需要我们去注意的。在下文中将介绍下异常处理、取消循环等话题。

目录
相关文章
|
6月前
|
关系型数据库 MySQL 数据库
找不到请求的 .Net Framework Data Provider。可能没有安装
做的一个项目,框架为.net framework 数据库为mysql 出现如标题错误 检查是否安装mysql、是否安装mysql connector net 笔者是因为没有安装后者mysql connector net 下载地址: [mysql connector net](https://downloads.mysql.com/archives/c-net/ "mysql connector net") 笔者安装截图如下: ![请在此添加图片描述](https://developer-private-1258344699.cos.ap-guangzhou.myqcloud.com/c
56 0
|
4月前
|
人工智能 机器人 C#
Windows编程课设(C#)——基于WPF和.net的即时通讯系统(仿微信)
一款参考QQ、微信的即时通讯软件。采用CS结构,客户端基于.Net与WPF开发,服务端使用Java开发。
|
7月前
|
人工智能 自然语言处理 程序员
.NET开发工作效率提升利器 - CodeGeeX AI编程助手
.NET开发工作效率提升利器 - CodeGeeX AI编程助手
133 0
|
7月前
|
人工智能 自然语言处理 程序员
.NET开发工作效率提升利器 - CodeGeeX AI编程助手
.NET开发工作效率提升利器 - CodeGeeX AI编程助手
105 1
|
9月前
|
SQL 开发框架 缓存
.NET的并发编程(TPL编程)是什么? (下)
.NET的并发编程(TPL编程)是什么? (下)
|
9月前
|
SQL 开发框架 缓存
|
11月前
|
人工智能 机器人 API
.NET面向AI编程——SK框架(SemanticKernel)的简易入门实践
随着ChatGPT开始在各个领域遍地开花,有关的应用也开始层出不穷。随着微软开源SK框架,无疑是给.NET开发者带来面向AI应用的新机遇。以下内容,通过创建一个简单的prompt开始,开发一个使用SK框架进行开发的入门教程,供参考。
207 0
|
12月前
|
缓存 前端开发 JavaScript
采用.Net Core技术框架开发的医院云LIS平台源码,B/S架构
基于B/S架构的医学实验室检验系统源码,整个系统的运行基于WEB层面,只需要在对应的工作台安装一个浏览器软件有外网即可访问。全套系统采用云部署模式,部署一套可支持多家医院检验科共同使用。 采用.Net Core新的技术框架、DEV报表、前端js封装、分布式文件存储、分布式缓存等,支持LIS独立部署,Docker部署等多种方式。
|
存储 Go 网络架构
Go HTTP 编程 | 02 - net/http 包剖析
Go HTTP 编程 | 02 - net/http 包剖析
Go HTTP 编程 | 02 - net/http 包剖析
C#编程-140:Net.Mail类发送邮件
C#编程-140:Net.Mail类发送邮件
C#编程-140:Net.Mail类发送邮件