Silverlight 的多线程能力(下“.NET技术”)

  上一期笔者介绍了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)的确定性算法笔者准备了两种:

  1. 试除法

具体方法是从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的两个算法类:

  1. 试除类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上海企业网站设计与制作yle="color: #000000;">) returnfalse;
  }
  return true;
}

其中FindPrime方法用于查找记录素数,查找起始点从_startNum开始,步进为_step。而IsPrime方法用于判断是否是素数,遍历从3开始到x的平方根的所有奇数能否整除x。

埃拉托斯特尼筛法类EratosthenesFinder

该类用于验证多线程下试除法查找的结果是否正确,其中主要函数FindPrime实现如下:


publicvoid FindPrime(uint MaxNumber)
{
_primeCount = 1;
bool[] Numbers = newbool[MaxNumber];
uint SqrtMaxNumber = (uint)(Math.Sqrt(MaxNumber -= 1u));
for (uint i = 1u; i < SqrtMaxNumber; )
{
    while (Numbers[i += 2u]) ;
    for (uint k = ((uint)(MaxNumber / i) + 1u) | 1u; k > i; )
    {
      while (Numbers[k -= 2u]) ;
      Numbers[k * i] = true;
    }
  }

  for (uint i = 3u; i < MaxNumber; i += 2u)
  if (!Numbers[i])
  {
    _primeCount++;
    _maxPrime = i;
  }

  //通知完成查找
  InvokeFindComplete(EventArgs.Empty);
}

  在主页面上,笔者创建了6个按钮执行不同的调用方式:

  1. 后台单线程执行Eratosthenes筛法进行验证
  2. 在UI线程直接查找
  3. 在后台单线程查找
  4. 基于ThreadPool的后台多线程查找
  5. 基于BackgroundWorker的后台多线程查找
  6. 基于Thread的后台多线程查找

  而对于DispatchTimer类,笔者将其作为定时器用于对后台线程状态的监控。具体方法如下:

  在主页面中加入定时器属性dt,并在页面加载事件中定义定时器时歇与Tick事件:


DispatcherTimer dt = newDispatcherTimer();
void MainPage_Loaded(object sender, RoutedEventArgs e)
{

  ……
  dt.Interval = newTimeSpan(0, 0, 0, 0, 10);
  dt.Tick += newEventHandler(dt_Tick);
}

void dt_Tick(object sender, EventArgs e)
{
  tbTimer.Text = (Environment.TickCount - StartTickCount).ToString();
  switch (CurrFinder)
  {

    caseUseFinder.EratosthenesFinder:
    tbPrimeCount.Text = (efinder.PrimeCount).ToString();
    break;
    caseUseFinder.SinglePrimeFinder:
    tbPrimeCount.Text = (singlefinder.PrimeCount + 1).ToString();
    break;
    caseUseFinder.MultiPrimeFinder:
    tbPrimeCount.Text = (multifinder.Sum(t => t.PrimeCount) + 1).ToString();
    tbIdleThread.Text = multifinder.Count(t => t.Stat == WorkerStat.Init).ToString();//闲置线程数
    tbWorkingThread.Text = multifinder.Count(t => t.Stat == WorkerStat.Working).ToString();//工作线程数
    tbWorkedThread.Text = multifinder.Count(t => t.Stat == WorkerStat.Worked).ToString();//完成线程数
    break
    ……
  }
}

  t_Tick事件定义了单线程与多线程下对查找过程和多线程工作状态的显示。而在所有查找方法调用前触发dt.Start()事件开始监控;在完成查找时,调用dt.Stop()事件停止监控。

  下面鉴于篇幅所限,我主要介绍基于ThreadPool的后台多线程查找的实现方式:


privatevoid btnMultiWorker_Click(object sender, System.Windows.RoutedEventArgs e)
{
  //设置当前查找器
  CurrFinder = UseFinder.MultiPrimeFinder;
  gdMultiThreadInfo.Visibility = Visibility.Visible;
  multifinder.Clear();
  CallBackThreadCount = 0;
  StartTickCount = Environment.TickCount;
  dt.Start();

  //构造多线程查找器
  for (uint i = 0; i < ThreadCount; i++)
  {
    //对余2i+1的同余类进行查找,步进为两倍线程数
    PrimeFinder afinder = newPrimeFinder(2 * i + 1, MaxNum, 2 * ThreadCount);
    afinder.OnFindComplete += (s1, e1) => Dispatcher.BeginInvoke(multifinder_OnFindComplete);
    multifinder.Add(afinder);
    ThreadPool.QueueUserWorkItem(state => afinder.FindPrime(), i);
  }
}

void multifinder_OnFindComplete()
{

  CallBackThreadCount++;

  //当全部完成时显示结果
  if (CallBackThreadCount == ThreadCount)
  {
    dt.Stop();
    tbLog.Text += string.Format("后台多线程试除法找到{0}内的素数共{1}个,最大素数为{2},耗时{3}毫秒(基于ThreadPool)\n", MaxNum, multifinder.Sum(t => t.PrimeCount) + 1, multifinder.Max(t => t.MaxPrime), Environment.TickCount - StartTickCount);
  }
}

  以上代码中gdMultiThreadInfo是显示多线程信息的Grid。而multifinder为查找器后台运行实例的集合,通过该集合可以汇总数据,比如通过multifinder.Max(t => t.MaxPrime)就可以找到最大素数。在构建多线程查找器时,我们按照用户输入的线程数ThreadCount 来构建同等个数的同余类,由于偶数不用查找,因此我们的步进可以是两倍于ThreadCount,这样同余类的余数刚好是2i+1(其中i从0到ThreadCount-1),让每个查找线程司职不同的同余类。这里我们通过ThreadPool线程池来加载每个后台线程,并将每个后台线程实例的OnFindComplete事件通过该UI实例的Dispatcher分发器属性的BeginInvoke方法委托给UI线程中的multifinder_OnFindComplete事件来完成。在完成的回调事件中如果全部数量的后台线程都完成就进行结果显示,其中multifinder.Sum(t => t.PrimeCount) + 1的原因是在试除法中没有考虑2这个素数。

  其他5种方式的实现不再赘述。现在我们开始实测,笔者在一台8核的PC机上进行了400万以内素数查找,结果如下:

  这里笔者设置了8线程同时测算,其多线程效率为单线程的7.5倍(接近8倍)。当设置线程数为16时,结果几乎保持不变,也就是说当多线程数量大于CPU核数时多线程也存在等待CPU时间片去调度的问题,而不能全部并行。经过测试工作线程数等于核数+1,在8核的PC上运行情况如下图所示:

  最后的结论是多线程最多能提升CPU核数倍,因此后台线程数刚好等于CPU核数时效率最优。到这里笔者还不想结束本期话题,因为大家对Silverlight的性能还没有一个横向对比。因此,笔者分别在JavaScript(IE9)和Flash CS5中完成了同样试除法的开发。

  JavaScript代码如下:


<scripttype="text/javascript">
function IsPrime(x) {
if (x == 1) returnfalse;
var sqrtx = Math.sqrt(x);
for (var i = 3; i <= sqrtx; i += 2) {
if (x % i == 0) returnfalse;
}
return true;
}

function FindPrime() {
var _maxNumber = this.tbMaxNum.value;
if (/^\d+$/.test(_maxNumber)) // 验证输入是否是整数
{

  var startwatch = new Date(); // 计时器初始化
  var _maxPrime;
  var _primeCount = 0;
  for (var i = 1; i <= _maxNumber; i += 2)
  {
    if (IsPrime(i)) {
      _primeCount++;
      _maxPrime = i;
    }
  }
  var t = new Date() - startwatch;
    tbLog.value += "JavaScript试除法找到" + _maxNumber + "内的素数共" + _primeCount + "个,最大素数为" + _maxPrime + ",耗时" + t + "毫秒\n"
  }else{
    alert("输入的不是整数!");
  }
}
</script>

  FlashCS5 ActionScript代码如下:


stop();

btnFind.useHandCursor = true;
btnFind.addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(event:MouseEvent):void {FindPrime();}
function FindPrime():void
{
  var _maxNumber = nsMaxNum.value;
  var startwatch = new Date();上海企业网站制作pan>// 计时器, 初始化.
  var _maxPrime;
  var _primeCount = 0;
  for (var i = 1; i <= _maxNumber; i += 2)
  {
    if (IsPrime(i))
    {
      _primeCount++;
      _maxPrime = i;
    }
  }
  var t:Number = Number(int(new Date()) - int(startwatch));
  taLog.text += "ActionScript试除法找到" + _maxNumber + "内的素数共" + _primeCount + "个,最大素数为" + _maxPrime + ",耗时" + t + "毫秒\n";
}
function IsPrime(x)
{
  if (x == 1)return false;
  var sqrtx = Math.sqrt(x);
  for (var i = 3; i <= sqrtx; i+=2)
  {
    if (x % i == 0)
    {
      return false;
    }
上海网站建设>  }
  return true;
}

  两种语言在同一台PC上的运行结果如下图:

  其中JavaScript在IE9中查找400万以内素数耗时3800毫秒,Flash CS5耗时35715毫秒,在同样算法、同一浏览器下三者性能对比如下表:

 
Silverlight多线程(8核8线程)


Silverlight单线程


JavaScript


Flash CS5


耗时(毫秒)


266


2016


3800


35715


性能比(倍)


134.8


17.7


9.4


1

  (注:以上性能对比仅供参考)

  本期示例地址:http://58.213.156.24/Demo

  源代码地址:http://58.213.156.24/Demo/Code

  通过本期对Silverlight的多线程能力分析,想必大家对Silverlight的多线程编程有了大概了解。笔者认为对于RIA应用而言为用户提供无等待的响应速度比更多的功能显得更为重要!

时间: 2024-10-21 22:46:34

Silverlight 的多线程能力(下“.NET技术”)的相关文章

一起谈.NET技术,Silverlight 的多线程能力(下)

上一期笔者介绍了Silverlight实现多线程的诸多解决方案,本期笔者将通过一个实例来实现所有多线程编程方法,并且还将于JavaScript和Flash两种Web客户端技术性能进行比较,请勿拍砖. 在正式编程前,笔者还要重申上期非常重要的观点:Silverlight多线程主要作用不是在于提高性能,而是在于用户体验.这里要给多线程泼一盆冷水了,多线程与性能提升不是正比关系,如果你使用一个单核CPU的客户端设备,那么即便你创建100个多线程也与单线程的计算性能是一样的,因为一个CPU时间片下只能处

一起谈.NET技术,Silverlight的多线程能力(上)

对于多线程其实一直以来都存在很多误区:比如多任务与多线程就很容易被混为一谈,而多线程也常被理所应当的认为是并行等等.而事实却是:多任务≠多线程.单任务≠单线程.多线程不一定并行,多线程与性能不成线性关系等等,其中道理在这里不再详述.笔者认为Silverlight多线程主要作用不是在于提高性能,而是在于用户体验,其根本目的是解决用户体验中的响应速度,减少单线程带来的阻塞问题.用一个贴切的例子来形容单线程和多线程的区别:单线程就好像只有一个服务窗口卖票的车站,人们排队买票时都是单线程处理的,而且不能

Silverlight“.NET研究” 的多线程能力(下)

上一期笔者介绍了Silverlight实现多线程的诸多解决方案,本期笔者将通过一个实例来实现所有多线程编程方法,并且还将于JavaScript和Flash两种Web客户端技术性能进行比较,请勿拍砖. 在正式编程前,笔者还要重申上期非常重要的观点:Silverlight多线程主要作用不是在于提高性能,而是在于用户体验.这里要给多线程泼一盆冷水了,多线程与性能提升不是正比关系,如果你使用一个单核CPU的客户端设备,那么即便你创建100个多线程也与单线程的计算性能是一样的,因为一个CPU时间片下只能处

Silver“.NET研究”light的多线程能力(上)

对于多线程其实一直以来都存在很多误区:比如多任务与多线程就很容易被混为一谈,而多线程也常被理所应当的认为是并行等等.而事实却是:多任务≠多线程.单任务≠单线程.多线程不一定并行,多线程与性能不成线性关系等等,其中道理在这里不再详述.笔者认为Silverlight多线程主要作用不是在于提高性能,而是在于用户体验,其根本目的是解决用户体验中的响应速度,减少单线程带来的阻塞问题.用一个贴切的例子来形容单线程和多线程的区别:单线程就好像只有一个服务窗口卖票的车站,人们排队买票时都是单线程处理的,而且不能

大数据是电子地图的基础,AI成为下一个技术风口

每到春运之时,人口的迁入和迁出就成了城市的主旋律.昨日,高德地图发布了2017年春运大数据.其中,广东省的5座城市在人口迁出比例排名中占据前5位,而安徽省的4座城市名列人口迁入比例排名前10位,成为返乡大省. 高德地图能够得到关于人口迁徙的大数据,是基于平时对用户数据的收集与累积.其实不止是高德地图,目前的电子地图全都离不开大数据的支撑,可以说没有大数据就没有电子地图. 电子地图的原理是什么?支撑电子地图的大数据究竟是如何得来的?采取怎样的方式才能收集到?这些问题都值得我们思索一番. 现在,人们

大数据治理需要具备哪些能力和关键技术?

在企业数据建设过程中,大数据治理受到越来越多的重视.从企业数据资产管理和提升数据质量,到自服务和智能化的数据应用,大数据治理的内容在不断发展和完善,其落地实施的过程中会遇到各种各样的难题和挑战.本篇文章通过分析大数据治理建设中的沟沟坎坎,总结出了大数据治理需要具备的能力和关键技术. 一.困难重重却充满光明的大数据治理发展之路 1. 传统数据治理一直无法逃脱的魔咒 大数据治理从建设内容和实施目标上可以划分成不同的阶段,每个阶段完成不同的任务,随着阶段的递进,建设内容逐步加深,不同的企业切入点和诉求

link环境下codefirst技术制作的《网盘软件》,如何绑定机器码,避免恶意注册?

问题描述 link环境下codefirst技术制作的<网盘软件>,如何绑定机器码,避免恶意注册? link环境下codefirst技术制作的<网盘软件>,如何绑定机器码,避免恶意注册? 解决方案 绑定机器码没用的,要绑定身份证手机或者信用卡.再说asp.net也没法绑定机器码,用户用ipad访问,你绑定什么鸟机器码.

多线程环境下单元测试的问题

问题描述 多线程环境下单元测试的问题 某个函数worker是在多线程环境下运行的,如果要对其进行"单元测试",是不是也要在相应的多线程环境下测试?还是在单线程环境下测试? 解决方案 多线程肯定要在多线程环境下测试,这样才能正确的模拟它的行为 解决方案二: 既然是多线程的,你就要在多线程的一个环境下对它进行测试才是真实的结果. 解决方案三: 首先,你还是需要在单线程下测试,单线程的单元测试对于多线程工况下的代码同样有意义. 其次,你应该做多线程测试,这个可以借助mock一类的框架构造仿真

剖析支付宝线下支付技术

支付宝目前支持的支付方式,被大家所熟知的主要有移动支付与条码支付两大类.移动支付包括买家直接在手机应用中购买商品或服务.买家选择支付宝支付.通过支付宝进入收银台进行支付等方式:而条码支付则是通过"扫一扫"实现的,现在众多大型超市都已支持扫码支付,极大方便了用户们"买买买". 而2016云栖大会首日,蚂蚁金服支付宝事业群资深技术专家竹锋向我们介绍了蚂蚁金服支付保在线下支付技术,包括当面付.第三方应用授权.口碑开店等.当面付需要双方在一起,且均已安装支付宝钱包,可通过条