博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DotNet并行计算使用误区(三)
阅读量:6081 次
发布时间:2019-06-20

本文共 3002 字,大约阅读时间需要 10 分钟。

 这篇文章主要是从实用的角度讲解并行计算需要了解的一些基础知识以及需要注意的地方,包括并行循环的方法、如何终止、线程安全、常用类型等几方面。

关于TPL中提供的并行方法,这里就不再多说了,网上有很多例子,本系列文章第三个Topic主要讲的不是“如何跑”,而是要讲一下“如何停”。

    曾经查过很多关于二者的资料,可能是我理解的原因,总觉得很少有对其解释正确的,所以我觉得还是有必要写出我的观点,请大家指正。

    Section 1.并行循环的终止:Break与Stop

    关于二者网上有很多解释,很多人都认为,在并行计算中:

    1.Break的调用会导致当前任务和已分配任务的终止

    2.Stop的调用会导致当前任务的终止

    本人对以上两点持怀疑态度,经过试验证明,Break和Stop并不像以上说的那样。

    这里先解释一下名词,“当前任务”指的是当前已经触发Break(Stop)条件的那个任务;“已分配任务”指的是与“当前任务”并行执行的任务;“未分配任务”指尚未开始的循环部分。

    有以下测试代码,这些代码在不同核数量的机器上返回的记录数是不同的,这一点待会再说,先看代码:

 
class
Program
{
static
List
<
int
>
Data
=
new
List
<
int
>
();
static
ParallelOptions opt
=
new
ParallelOptions();
static
void
Main(
string
[] args)
{
opt.MaxDegreeOfParallelism
=
Environment.ProcessorCount;
for
(
int
i
=
0
; i
<
10
; i
++
)
{
Data.Add(i);
}
Console.WriteLine(
"
CPU Degree:
"
+
opt.MaxDegreeOfParallelism);
Console.WriteLine(
"
GeneralFor Result:
"
);
new
Program().GeneralFor();
Console.WriteLine(
"
ParallerStop Result:
"
);
new
Program().ParallerStop();
Console.WriteLine(
"
ParallelBreak Result:
"
);
new
Program().ParallelBreak();
Console.Read();
}
//
一个普通的For循环
private
void
GeneralFor()
{
for
(
int
i
=
0
; i
<
Data.Count; i
++
)
{
if
(Data[i]
>
5
)
break
;
Console.WriteLine(Data[i]);
}
}
//
并行计算的Stop
private
void
ParallerStop()
{
Parallel.For(
0
, Data.Count,opt, (i, LoopState)
=>
{
if
(Data[i]
>
5
)
LoopState.Stop();
Thread.Sleep(
10
);
Console.WriteLine(Data[i]);
});
}
//
并行计算的Break
private
void
ParallelBreak()
{
Parallel.For(
0
, Data.Count, opt, (i, LoopState)
=>
{
if
(Data[i]
>
5
)
LoopState.Break();
Thread.Sleep(
10
);
Console.WriteLine(Data[i]);
});
}
}

 

    下图分别是程序在2(32#)、4(64#)、48(64#)核CPU下运行的结果:

  

    个人认为,不同CPU核数量对于使用Break与Stop终止循环形式的最终记录返回数量是有影响的,即这会影响TPL分配任务的方针,TPL在运行时才会“源源不断”的分配任务,开启的线程数也是递增形式的。(最大线程数应该有限制,具体是多少不确定,有的说是64也有的说是256,等待高人解答)

    所以,对于Stop与Break,我的观点是,在并行任意任务中调用Stop方法,则会终止除当前任务外的所有并行任务(包括未分配的和已分配的),返回最终结果,而不是像传说的那样,终止了当前任务;Break只会停止继续分配新任务,并不影响当前任务的和已经分配的并行任务的执行,而且同样不会终止当前任务。

    Stop更改ParallelLoopState 对象的 IsStop值为true;

    Break更改ParallelLoopState 对象的 LowestBreakIteration 属性值等于 true 。
  
 

未分配的任务(未开始的任务)

已分配的任务(并行中的任务)

当前任务(触发条件的那个任务)

单行中的Break

停止分配

停止执行(相当于“当前任务”)

停止执行

Break

停止分配

继续执行

继续执行

Stop

停止分配

停止执行

继续执行

 

    如上图中显示,调用Stop后,结果中只能可能有一个大于5的结果,因为此时未分配和已分配的任务都被终止了,而当前任务并没有停止,继续执行打印语句,打印出来的只是当前的那个大于5的任务;

    而调用Break后,结果中大于5的结果数量不定,因为当当前任务因满足大于5的条件,而触发Break后,其他已分配的任务并不会停止,即使它们包含大于5的任务,同样也会打印出来,这就是上图中第三幅图中,Break任务出现两个大于5的结果的原因!

    Stop和Break,它们的区别可以用下图表示:

    图中实现表示运行的任务,虚线表示任务运行过程中被终止。

    Section 2.线程安全

    在并行计算中应当使用线程安全的类,例如有些时候我们需要不断迭代形成一个集合组织,这个集合可能是一个列表,在普通程序中List<T>完全可以完成这个任务,但是如果在并行计算中使用List<T>的add方法,就会出现一些错误,这些错误是随机的,也就是说有时候并不出现。

    以下代码:

 
List
<
string
>
ls
=
new
List
<
string
>
();
Parallel.For(
0
,
10000
, (i)
=>
{
ls.Add(i.ToString());
});

    以上代码偶尔会出现错误,出现错误的概率随着循环次数和并行任务的增加而增大,类似代码需求可以使用ConcurrentBag<T>来代替,针对并行计算提供的类库请参照MSDN:

    提示:并行计算并不是天上掉下的馅饼,它不是所有时候都比串行程序快,因为并行总要付出一些额外的代价,比如任务分配、任务同步、任务通讯等,到底什么时候才能放心的吃掉这个馅饼,需要仔细地设计算法,并且应该在多台典型的服务器环境中进行测试、对比,这是一个比较烦人的过程,但是最终得到的结果可能会改变你的设计思路。

 本文转自Aicken(李鸣)博客园博客,原文链接:http://www.cnblogs.com/isline/archive/2011/06/27/2091079.html,如需转载请自行联系原作者

你可能感兴趣的文章
【Cloud Foundry 应用开发大赛】“八卦街”后台管理系统
查看>>
ice版本resize 错误调试(Host key verification failed)
查看>>
http://fulinweiyang.iteye.com/
查看>>
php ssh2
查看>>
状态码
查看>>
Git合并多个 Commit
查看>>
结合Spring和Groovy解决脚本热加载
查看>>
【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
查看>>
SpringMVC内部跳转与重定向
查看>>
oracle 创建,删除存储过程,参数传递,创建,删除存储函数,存储过程和函数的查看,包,系统包...
查看>>
基础代码随笔
查看>>
如何实现SVN导出某段时间修改过的文件;自动将class文件部署到服务器中;重启服务器...
查看>>
PropertyUtils、BeanUtils的copyProperties()和set、get性能比较
查看>>
2014-11-10 关于C# 如何获取本地电脑的UTC时区
查看>>
Ceph的基本情况进行概要介绍
查看>>
MySQL数据库优化思路之配置文件优化
查看>>
分享纯 nginx 防御代码 nginx safe.conf
查看>>
Python-求素数程序
查看>>
如何去掉ECSHOP meta标签中的版本号
查看>>
【重构与模式】6.3 用Factory封装类
查看>>