|
上一期笔者介绍了Silverlight实现多线程的诸多解决方案,本期笔者将通过一个实例来实现所有多线程编程方法,并且还将于JavaScript和Flash两种Web客户端技术性能进行比较,请勿拍砖。
在正式编程前,笔者还要重申上期非常重要的观点:Silverlight多线程主要作用不是在于提高性能,而是在于用户体验。这里要给多线程泼一盆冷水了,多线程与性能提升不是正比关系,如果你使用一个单核CPU的客户端设备,那么即便你创建100个多线程也与单线程的计算性能是一样的,因为一个CPU时间片下只能处理一个线程,多线程也必须串行处理,甚至还可能因为过多的CPU调度开销而导致性能不及单线程的情况。当然在多核的情况下多线程可以负载到多个CPU上并行执行而提升性能,经过笔者在项目实施前的技术研究中发现如果客户端有N核的情况下,Silverlight多线程可以被N个CPU时间片平分,而CLR将同时让N+1个线程处于Ready状态,经过反复测试多线程性能是单线程的近N倍。其实客户端已经呈现多核趋势,就在不久前发布了PSP的下一代产品NGP采用ARM 4核处理器,而iPad2采用A5双核处理器,而我们现在用的笔记本与台式机基本都是超过2核的处理器,所以多线程的计算能力还是很有前景的。
下面我们就一起来看看实例,这个实例笔者选择了比较容易懂的素数计数函数(Prime-counting function)作为实例,用数学专业术语来说就是π(x),有没有搞错怎么和圆周率有关?这里不是圆周率而是π函数,是一个用来表示小于或等于某个实数x的素数的个数的函数。比如π(10)=4,因为不大于10的素数有2,3,5,7共计4个。对于π(x)的确定性算法笔者准备了两种:
- 试除法
- 埃拉托斯特尼筛法
具体方法是从3开始对所有不大于x的奇数进行素数判断。当判断i是否为素数时,通过从3开始到i的平方根(i=m*n中必然有一个因子小于i的平方根)的所有奇数进行试除,如果i能被整除则i不是素数,否则i是素数。该算法最易理解,而且可以并行试除,并行试除法的思路是按照2k*m+n的同余类进行分组,如果有k个并行组,那么对于从3开始对所有不大于x的奇数可以用{2k*m+1,2k*m+3,…,2k*m+2k-1}共k个同余组来分别进行试除,最后π(x)等于所有分组素数求和。
埃拉托斯特尼筛法,简称埃氏筛或爱氏筛,是一种由古希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法,该算法的思路从第一个素数开始,按照素数的倍数都是合数的思路,全部筛去,然后再筛去第二个素数的倍数,一直到当前素数大于x的平方根时结束,所得到没有筛去的数都是素数。该算法是已知确定性算法中时间复杂度最低的算法,但缺点是不能并行(至少笔者目前还没有找到并行筛法,如果你找到了请与笔者联系)。
在本案例中笔者使用试除法进行多线程计算,并通过筛法来校验计算的正确性。下面我们首先实现Silverlight的两个算法类:
- 试除类PrimeFinder
该类主要负责对并行算法的支持,其中MaxPrime属性用来记录最大素数,PrimeCount属性记录素数个数,Stat属性的类型为枚举类WorkerStat { Init, Working, Worked },用以监视线程的工作状态。OnFindComplete事件用于通知UI线程查找完成。其中主要函数实现如下:
publicvoid FindPrime()
{
_primeCount = 0;
_stat = WorkerStat.Working;
for (uint i = _startNum; i <= _maxNam; i += _step)
{
if (IsPrime(i))
{
_primeCount++;
_maxPrime = i;
}
}
_stat = WorkerStat.Worked;
//通知完成查找
InvokeFindComplete(EventArgs.Empty);
}
privatebool IsPrime(uint x)
{
if (x == 1u) returnfalse;
uint sqrtx = (uint)(Math.Sqrt(x));
for (uint i = 3u; i <= sqrtx; i += 2u)
{
if (x % i == 0) returnfalse;
}
return true;
}
NET技术:Silverlight 的多线程能力(下),转载需保留来源!
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。