日志分类:.Net技术

RIA之战 微软欲借开源策略后来居上?

2010年05月18日 7:46 上午  |  分类:.Net技术

        近10年以来,人们一直在试图寻找一个更好的Web开发的解决方案。最初是HTML和CSS,后来又有了AJAX和Web 2.0。但是因为HTML模型是基于页面的模型,缺少客户端智能机制,所以到目前为止基于HTML的Web 应用程序对完成复杂应用方面始终跟不上步伐,整体的用户体验效果与桌面应用程序仍然有差距。

  微软和Adobe作为Web应用领域的两大巨头,主要是采用为Web应用程序植入插件的方式来巩固Web战略。近日,在美国旧金山举行的开源大会(Open Source Business Conference)上,微软在“Web为平台”专家讨论会上宣布了针对社区群体的RIA新开源策略,将公开其RIA技术Sliverlight 的源代码。难道是巨人转性,还是另有隐情?开源策略在微软RIA战役中扮演什么样的角色?

  一.开源RIA将引导微软走向开源时代

  (1)RIA将成为互联网的主流

  在互联网诞生的时候,大家都在琢磨怎么把信息通过网络主动发布出去,出于共享信息的简单目的,一种快速小型超文本语言(HTML)被创建了。历经了多次的修改和完善,众多与Internet相关的技术纷纷出现,从DHTML、XML到Java Applet、SWT、AJAX、Flash等,这些技术有些是平行发展,有些是一脉相承。但毫无例外的都追求着一个共同的目标,就是更加强大、更高效反应、更加灵敏和更精彩的可视化特性的互联网程序。

  也就是说,在过去几年中,Web开发人员一直想构建一种比传统HTML更丰富的客户端,要实现比用HTML实现的接口更加健壮、反应更加灵敏和更具有令人感兴趣的可视化特性。这时,RIA技术出现了,它允许我们在互联网上以一种像使用Web一样简单的方式来部署富客户端程序。RIA是一种互联网应用程序, RIA目前在很多地方既指富互联网应用系统,又指富互联网应用系统的开发技术,我们也可以将其理解为下一代互联网的应用程序。

  那么,RIA将来会成为互联网的主流么?这是一个只有一个答案的问题,那就是“会”。因此,一场新的技术战争已经悄然在RIA领域打响了。毫无疑问,Adobe的Flash是RIA技术领域中最具优势的选手,但是微软借助Silverlight技术正在改变这种情况。例如,Silverlight可以运行在所有Web浏览器上,而不仅仅是微软的IE浏览器;而且Silverlight还采用了打破微软多年老规矩的开源策略。

  (2)微软开放Silverlight源码,向开源社区示好

  一直对开源吝啬、不感冒的微软居然也一反常态地对开源社区示好,公开其RIA技术Sliverlight 的源代码,是巨人转性还是另有隐情呢?这当然不能仅仅只用巨人转性来解释,也不是仅仅是因为 Adobe 宣称将开放其用于 Flex RIA环境下的软件开发包代码的回应。据有关专家分析,微软公布Silverlight技术的部分源代码,以此表示对开源组织的友善态度,目的是为了籍此吸引开发社区的关注,更好地同Adobe展开竞争,更是其欲称霸Web平台领域的一种新姿态。因为居于 RIA 开发工具领先地位的 Adobe,也正希望借开放其 Flex 部分源码来巩固优势,而作为挑战者的微软想要赶上 Adobe,当然也必须要借助开源来吸引开发者使用 Silverlight。

  微软此举从策略上来讲,可称为是实现在网络领域的一次飞跃。当然,开源并没有微软以前想的那么可怕,让Silverlight开源反而将更有好处:它将极大地扩展 Silverlight 的市场,以最快的速度普及。当 Silverlight足够普及时,微软必定会看到更多的新利益。实际上,微软的许多对手都是携着开源的力量来与微软竞争的,如Google 携开源势力强势入侵微软的多个传统强势领域,开源的 Firefox 又在不断抢占微软 IE 的市场份额,而这次 Adobe Flex RIA也打起了开源的主意,微软终于坐不住了,也破天荒地宣布 Silverlight 的开源策略。对于未来的展望,我们有理由相信只要微软尝到了 Silverlight 开源的甜头,微软就可能会尝试更多的开源计划,这也可能会成为使微软加入开源阵营的一个起点。

  二.为什么微软RIA需要采用开源策略?

  在RIA市场中,选用哪家的工具,一直不是最关键议题,重点是在于开发者是在哪一个RIA生态环境中。因为为了确保RIA可与内部的核心系统相连结,一个认同某个RIA生态环境的企业决策主管,极可能会下达或建议开发人员选择哪家的开发RIA工具。在这样的状况下,可以预见对RIA生态环境的认识和习惯将会成为Web开发决策的关键因素。

  据Forrester公司的分析师表示,在RIA市场上两大巨头Adobe和微软各具优势。Flash先入为主,目前已经有了一个很大的市场,Flash技术已经应用于90%左右的PC上。微软想要拉拢这些真正的市场推动者则要花上不小的力气,而微软在RIA技术Silverlight上应用开源策略正是出于拉扰这些市场推动者的考虑。那么,为什么微软Silverlight 的发布与推广需要采用开源策略呢?

  (1)Web开发者需要培训

  从用户体验的角度来说,我们甚至还没有发挥出HTML的全部潜力。因此,大部分开发者认为基于标准的Web开发还大有潜力可挖,通过加强设计者与开发者的联系,Web产品的用户体验可以得到很大的提升。目前大部分Web应用还不能令人满意,但是这个问题并不完全归咎于浏览器的非标准实现,更多的是因为设计和开发者本身对Web技术的掌握还不到位。

  一般来说,Web设计者往往感性些,通常是半技术性人员,喜欢定期购买和升级软件产品。而开发者则更理性,是纯技术性人员,卖给他们工具非常困难。和多数工程师一样,开发者通常更喜欢自己创建工具,或者使用免费提供的开源工具。开发者之所以更接受开源,是因为开源可以让他们控制自己使用的工具。因此,微软只有通过开源的方式才有望追赶Adobe。

  (2)做大RIA市场规模,需要更广泛的协作

  Adobe已推出多年的Flash技术与Flex工具是公认比较成熟的RIA解决方案,拥有绝大部分的市场占有率优势;而微软推出的Silverlight技术是挟程序开发领域的优势,进入网页应用市场。但为让RIA市场更为蓬勃发展,则需要建立起更广泛的RIA生态体系,这意味着微软不能只依赖设计人员或开发人员等单一族群。所以,通过开源策略可让RIA的开发人员和设计人员迅速理解RIA内部运行机制。因为就技术人员来说,开源可以极大的提高人员的技术水平,通过对开源的学习可以以最快的速度对很多基本的东西加以理解。

  另外,开源技术的灵活性可使得它能够比专有解决方案更易于添加更多自由和个性化的功能。这是因为开源技术的开发、测试和发布过程完全是透明的,同时提供的源代码及完善的文档,有助于开发者清楚地了解开源技术的工作原理和实现方法,也更容易得到质量更好的实现方案。这就保证了开源技术除功能上不逊于封闭源代码的方案外,还具有更高的灵活性,以及更低的采购和使用成本。因此,开源能对整个RIA开发生态环境的技术发展起到极大的推动作用。

Windows Azure入门:使用Queue Storage

2010年05月18日 7:44 上午  |  分类:.Net技术

      本文将会介绍如何使用Queue Storage。Queue Storage提供给我们一个云端的队列。我们可以用Queue Storage来进行进程间的相互通信(包括运行在不同机器上的进程之间的通信)。一个使用Queue Storage经典的场景是,在一个Web应用程序中,用户通过表单递交给服务器数据,服务器收到数据后将进行处理,而这一处理将花费很多时间。这种情况下,服务器端通过Queue Storage可以把用户递交的信息存储在队列中,后台再运行一个程序从队列中取得数据进行信息的处理。以往如果程序时运行在Windows操作系统上,那么我们可以使用MSMQ来做类似的工作。而Queue Storage的出现为我们提供了另一种选择。特别是在非Windows操作系统上,我们依然可以使用Queue Storage的REST API来很方便地使用它。  

       为了方便.NET开发人员,我们在SDK中提供了Microsoft.WindowsAzure.StorageClient类来帮助发送REST请求。

  步骤一:创建解决方案和项目

  由于我们要在本地模拟环境下测试Queue Storage,首先,请确保Development Storage的管理器程序已经启动。我们可以找到管理器的进程手动启动或者让Visual Studio帮助我们启动他。

  右击工具栏中Development Fabric的图标,选择”Show Development Storage UI”。弹出如下图所示的窗口:

  我们要关注的是Service management中Queue所在的一行。要确保Status为Running。

  确认完毕后启动Visual Studio,并且新建两个Console项目。我们将演示如何在一个Console程序中往Queue Storage中添加信息然后另外一个Console程序如何读取并处理信息。

  步骤二:添加SDK程序集引用

  在两个Console项目中均添加对C:\Program Files\Windows Azure SDK\v1.1\ref\Microsoft.WindowsAzure.StorageClient.dll的引用。该路径为SDK默认安装路径,如果你不能在这个路径中找到Microsoft.WindowsAzure.StorageClient.dll请从SDK安装路径中寻找。

  步骤三:添加代码

  首先在两个项目中的Program.cs中均引用命名空间:

using System.Threading;

  using Microsoft.WindowsAzure;

  using Microsoft.WindowsAzure.StorageClient;

 

    然后在其中一个项目(为了叙述方便,后面称之为Client项目)的Main方法中加入如下代码,我们将用它来向Queue Storage中添加信息。

static void Main(string[] args)

  {

  var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;

  var queueStorage = storageAccount.CreateCloudQueueClient();

  // 检查名为helloworldqueue的队列是否被创建,如果没有,创建它

  var queue = queueStorage.GetQueueReference(helloworldqueue);

  queue.CreateIfNotExist();

  Console.WriteLine(Client is running…);

  while (true) {

  // 插入数据到队列中

  queue.AddMessage(new CloudQueueMessage(string.Format(client sent information: {0},DateTime.UtcNow.ToString())));

  // 每次插入数据后线程休息3秒

  Thread.Sleep(3000);

  }

  }

接着在另外一个项目(为了叙述方便,后面称之为Server项目)的Main方法中加入如下代码,我们将用它来从Queue Storage中读取信息和进行处理。

static void Main(string[] args)

  {

  var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;

  var queueStorage = storageAccount.CreateCloudQueueClient();

  // 检查名为helloworldqueue的队列是否被创建,如果没有,创建它

  var queue = queueStorage.GetQueueReference(helloworldqueue);

  queue.CreateIfNotExist();

  Console.WriteLine(Server is running…);

  while (true)

  {

  // 从队列中读取一条信息

  // 收到信息后可以根据收到的信息做处理,为了演示方便我们这里只是把信息显示出来

  // 在云端发送消息后这条消息将对于后续的请求不可见,但是并未被删除。我们需要显示删除它。

  // 否则在一段时间后该消息将重新可见。这一设计的好处是确保了所有消息都能够被处理。

  // 如果程序在收到消息后处理消息前就异常终止了那么数据依然在一段时间后可以被重新处理。

  // 详情请参考MSDN文档

  var message = queue.GetMessage();

  if (message != null)

  {

  Console.WriteLine(string.Format(Message retrieved: {0}, message.AsString));

  // 处理完数据后必须显示删除消息

  queue.DeleteMessage(message);

  }

  // 每次读取数据后线程休息3秒

  Thread.Sleep(3000);

  }

  步骤四:观察并分析代码

  步骤三中的代码中,首先我们通过CloudStorageAccount.DevelopmentStorageAccount来说明我们使用的本地的Development Storage自带账户而不是真正的云端存储服务账户。(如果要用真实账户可以使用

  CloudStorageAccount.Parse(”DefaultEndpointsProtocol=https;AccountName=[用户名];AccountKey=[密码]“);

  //DefaultEndpointsProtocol=https可以改成DefaultEndpointsProtocol=http表示用HTTP而不是HTTPS

  来实例化对象)然后通过该账户类来实例化一个Queue客户端类。这两步是使用SDK中StorageClient程序集来调用Queue Storage服务的必要步骤。

  Client项目中的代码相对简单。大约每隔3秒向Queue Storage中插入一条数据。Server端的项目中我们则大约每隔3秒从Queue Storage中读取一条数据。处理完毕后将之删除。对于为什么必须在读取后显示删除的问题请参考注释部分。

  步骤五:运行程序

  先运行Client程序然后运行Server个程序。如果一切正常,你将会看到Server项目的Console程序输出收到的消息:

  注意由于是本地模拟,Client和Server程序需要要在同一台机器上运行。但是如果使用真实账户的话,Client和Server程序就可以跨越机器进行通信了。

抛砖引玉:我看微软.NET各子技术领域之应用前景

2010年05月15日 8:45 上午  |  分类:.Net技术

     从2002年发布.NET 1.0,历经8年发展,.NET发展到了4.0,已经成为一个庞大而复杂的软件开发与运行平台,其架构日益复杂,其应用领域也在不断地扩展,包容了“一堆”的子技术领域。在.NET 4.0即将发布之际,回顾一下已发布的各项.NET技术,看看哪些技术用得很火,哪些被打入冷宫,再猜猜.NET 4.0中可能会有哪些技术会得到“青睐”,是件有意思的事。

1 桌面应用程序开发技术( Windows Form和WPF)。

  在.NET桌面应用程序开发领域,Windows Form是“前辈”,相比以前的老祖宗MFC,其开发效率高得多,即使比一向以“高效率”著称的VB、Delphi之类,也不逊色,因此在很长的一段时间内,Windows Form成为.NET 桌面领域的主流技术,而且有一大批各式各样的第3方控件,其功能可谓应有尽有,使用方便。

     Windows Form的问题是“千人一面”,要想做出“与众不同”的界面,真得费不少力气。

     .NET 3.0中出现的WPF,在界面设计和用户体验上比Windows Form要强得多,比如其强大的数据绑定、动画、依赖属性和路由事件机制,都非常棒。然而,WPF最头痛的是性能,另外,在需要快速开发原型的场景,WPF暂时还比不上Windows Form方便。

  .NET 4.0中,WPF在性能上有较大的改进,这点在使用WPF开发的Visual Studio 2010上体现极为明显,Visual Studio 2010 CTP和BETA1只能用“惨不忍睹”一词来形容,BETA2就有一个性能上的飞越,但还是不是地玩点“崩溃”、“挂死”的把戏,而当前的RC版本,我觉得其使用体验已经超越了VS 2008。

  我认为,WPF取代Windows Form是必然的

2 数据存取技术

  (1)ADO.NET。这不用多说了,在实际开发中用得太多了,事实证明了它的成功。

(2)LINQ。

    这也是个很大的领域,里面最牛的是LINQ to Object,我一用就喜欢上了。

     LINQ to XML也很好,它把程序员从代码中解放出来,可以完成大部分XML存取功能,让大家很高兴有机会能和原先.NET所提供的“一堆”XML相关类说声“不见”。

    LINQ to DataSet。作为一个ADO.NET技术的补充,这是一个无足轻重的小卒子,在开发中可以用,也可以直接忽略。

     LINQ to SQL和ADO.NET实体框架。这两个技术功能重叠,基本上让人怀疑其中有一个是不是“没有存在的必要”,所以曾有“LINQ to SQL已死”的传言。当然,后来微软公司表态说仍然会继续开发LINQ to SQL的后续版本,争论平息。

     但我个人觉得,在实际开发中还是使用ADO.NET实体框架更合适。LINQ to SQL有的功能它都有,而且用起来更为灵活,难得的是它的使用并不比LINQ to SQL复杂多少。

     ADO.NET实体框架还延伸到了其它的技术领域,是一项重要的基础数据存取技术。

     因此, ADO.NET实体框架 vs LINQ to SQL,前者胜出

(3)WCF Data Service。

     这是一项非常值得关注的技术,原先叫ADO.NET Data Service,它体现了“数据是一种服务”的思想,让数据可以通过HTTP请求直接获取,它设计了一套URI模式,可以完成投影、选择、分页等功能,用起来方便灵活。

     我觉得在SOA大行于世的分布式系统时代,WCF Data Service应该会得到应用。

     但这一技术问题在于性能。由于数据需要走互联网,所以如果网速很慢的话,基于此技术搭建的应用程序其用户体验将“惨不忍睹”。而且,互联网服务安全问题非常关键,保证基于WCF Data Service技术搭建的应用程序数据安全,想必将成为开发者最费脑筋的地方。

(4)WCF RIA Service。

     这个技术与Silverlight密不可分。我还没有系统地了解这一技术领域,不予评说。

3 Web开发技术

     这一领域,没说的,ASP.NET中的Web Form是当之无愧的主流。经过多年的发展,Web Form已高度成熟。VS 2008中加入的AJAX系列组件,如ScriptManger、UpdataPanel之类,再配合一堆的应用了AJAX技术的控件,让Web Form更是如虎添翼。基于这种成熟技术开发Web网站,不管是用户还是开发企业,都比较放心。

     从.NET 3.5 SP1开始,Web领域多了些新东西。

(1)ASP.NET MVC。MVC这一设计模式已有多年的历史,也有很多的成熟的框架,但在.NET“官方”平台上,却是个新加入的“成员”,并不算成熟,我觉得其应用前景要看看再说。我不知道业界是否已有基于此技术开发的实际项目,有这方面项目经验的朋友,不妨谈谈自己的看法。

(2)ASP.NET Dynamic Data。这是一个看上去很酷的技术。当使用它来创建网站时,Visual Studio 2010会帮你创建一个DynamicData文件夹,里面放了数十个模板文件,构建了一个网站的“脚手架”,几乎不用编码,就可以生成一个全功能的“CRUD”数据驱动网站。

     它的设计思想很好:底层使用ADO.NET实体框架或LINQ to SQL构造数据模型,通过提取数据模型中的元数据,动态选择合适的模板生成网页。这就避免了真实项目中不得不为每个数据存取任务设计不同网页的负担,而且这一技术提供了很多的方式去允许你定制网站。

     我当初刚一接触时,也很兴奋,这是个好东西啊!但后来我改变了看法,这一技术的问题在于它过于“自动化”了,而且需要包容数十个文件,让其与现有的ASP.NET网站集成相当不便,配置起来麻烦。

     我个人认为,在现有.NET Web开发技术应用现状之下,任何一个与现有的ASP.NET网站(以Web Form+AJAX为主体技术)集成麻烦的技术,都很难有“美好”的前途。很不幸,ASP.NET Dynamic Data是这样的例子,ASP.NET MVC也有同样的问题,但没有ASP.NET Dynamic Data严重,而且ASP.NET MVC架构清晰,还是比较易于维护。

(3)Silverlight。这实际上是另一种Web应用架构的代表技术,其立足点在于充分利用客户端的计算资源,可以大大地降低对服务端的依赖,而且易于构造良好的用户体验,我个人认为其发展大有可观。是一个需要重点关注的技术。

4 插件技术

     .NET 4.0引入了一个“Managed Extensibility Framework(MEF)”,我在此郑重推荐!

     MEF通过简单地给代码附加“[Import]”和“[Export]”标记,我们就可以清晰地表明组件之间的“服务消费”与“服务提供”关系,MEF在底层使用反射动态地完成组件识别、装配工作。从而使得开发基于插件架构的应用系统变得简单。够酷的技术!

     另外,请忘记.NET 3.5所引入的“MAF(Managed Add-in Framework )”吧,MAF引入了一个复杂的宿主与插件间的通讯管道架构,仅仅是创建一个最简单的SayHello宿主和插件,你也必须创建多达8个项目!

     最要命的是MAF设计者“想”得过多,设计了复杂的接口和类继承体系,而且选择让插件运行于与宿主不同的应用程序域中,这就使得插件与宿主之间的通讯变得复杂。个人认为,这些实在不是一个好的设计决策。

     我估计,MAF会“无疾而终”

5 WCF和.NET Remoting

     其实这是一个不需要讨论的问题,有了WCF,我还要Remoting干什么? 因为前者包容后者的所有功能,而且还提供了更多。

     WCF的问题是微软企图用一个框架解决所有的问题,因此其架构非常复杂,任何一名想探究其底层运行机理的人,都必须要有足够的心理准备和耐心。

     我们可以看到WCF向其它领域的渗透,比如前面的WCF Data Service,还有Workflow Service(将工作流发布为WCF服务),看来微软是将“宝”押在WCF上了,凡是带有“服务”字样的,微软都有把它改造为WCF服务的冲动。

     因此,WCF是不得不学习和掌握的技术。

6 多线程与并行计算

     关于并行计算,我已经写过不少文章了,废话少说,在多核时代,我认为.NET并行计算中的任务并行库和并行LINQ,会得到较多的应用。

7 工作流

     这个技术,我看是微软自己把事弄砸了。工作流从.NET 3.0开始引入,到.NET 3.5已经比较完善了,也有了一些实际的应用。但.NET 4.0就来了个另起炉灶,WF4与WF3.5相比,简直是另一个产品,而且WF4的BETA1和BETA2相比,居然在对象模型上也有大的改动,RC版本中的WF4我还没看,不知又有什么变动,应该不会再变了吧?!

     对于这样一个“变色龙”,谁用谁胆大。

8 函数式编程语言F#

     函数式编程很有趣,VS 2010中F#成为.NET正式成员。F#中的许多特性,比如不可更改(immutable)的数据结构,声明性编程风格,强大的类型推断,所有东西都是表达式等,都让习惯了面向对象风格的程序员感到新奇。

     我个人觉得,F#如果用于开发多线程并行计算程序,会有较高的开发效率,而函数式编程的特点,也会使它在科学计算中有较好的表现。但用于开发CRUD之类的MIS系统,至少目前还是免谈吧。

9 云计算

     Visual Studio 2010集成了云计算开发的项目模板。

     云计算是一个说不完的话题。微软在这方面投入巨大。它精心打造了Azure这个云计算平台。了解Azure的最佳方法是看“DAVID CHAPPELL”的文章《INTRODUCING WINDOWS AZURE》,这篇文章可以在微软网站上找到。

     虽然我个人认可云计算是一个大的发展方向,但对于中国,这个技术是一道远方的亮丽风景,仅供观赏。因为国内还没有一个成熟的云计算平台,而微软的Azure目前又没有开放中国大陆的云计算购买服务,加上中国又有特殊的国情,所以一切都只是空中楼阁。

VS2010智能跟踪 加速.NET应用程序调试

2010年05月12日 9:02 上午  |  分类:.Net技术

        Visual Studio 2010 Ultimate 版本有个新功能IntelliTrace(智能跟踪),IntelliTrace被引入到Visula Studio中来加速我们对.NET应用程序的调试,它通过对预先设置的事件和方法在运行过程中的跟踪并将其有效地传递给调试执行者,从而快速的传递程序在执行过程中的状态和各种信息来帮助开发者更好的调试程序,快速的发现问题。实际上,在Visual Studio 2010之前,Microsoft Visual Stuido已经帮助我们很好的继承了调试工具,比如对变量的监控,对堆栈的查看等等,并且允许设置断点进行单步调试等。所有的这些都在很好的帮助开发者来了解程序在不同的执行过程中的状态以及检测是否按照预先实际的逻辑进行运转,而且更多的,我们通过断点后监测程序状态来发现问题所在。而这样所存在的问题是,一些逻辑上的错误可能发生在断点执行之前,而且很可能是一些无法追查。开发者必须停止当前的调试,重新设置断点并启动调试,而这样的试验过程往往需要很多次才能找到正确的位置来设置断点。IntelliTrace智能的将一些调试信息和程序状态自动的跟踪并实施的展现给开发人员,从而减少了需要程序员找到适当的断点才能跟踪和监控程序运行状态的过程。

  Visual Studio提供给我们两个方式来控制其搜集信息的内容源,一个是IntelliTrace events only,仅仅收集智能跟踪事件和调试中断的相关数据;另外一个是IntelliTrace events and call information,这个就会收集除了IntelliTrace事件之外对一个方法调用的进入和退出的各种数据。下边我们来看看在Visual Studio 2010中如何设置IntelliTrace的跟踪选项。

  进入到工具 –> 选项 –> IntelliTrace。它在默认状态下是启用的,但是,仅捕捉事件,因为捕捉事件是便宜的。然而,当你开始跟踪所有这些调用信息时,真正的价值开始显现出来了。你每一次调试的时候,IntelliTrace都将弹出来,你在动态调试的时候可以使用这个历史性的调试工具。假如我在那里并且有一个断点,但是,我要备份和重新设计这个断点,我可以使用IntelliTrace完成这个任务。

  使用IntelliTrace捕捉到调试进程的其他人可以把记录以及.dlls和pdbs文件发送给你,你可以重放它们的进程。这个进程也许进入到数据库(你没有这个数据库),它也许是采用你不能复制的方法设置的,它可能在你不能企及的网络上,但是,你不用担心,因为你拥有记录以及.dlls和pdbs文件,你可以重放。

  当然,拥有源代码会更好,因为那样你就能够看到这个代码好像是在你自己的机器上调试它一样。这对于在你的机器上运行正常,在其他人的机器不能运行的那些神秘的故障来说是非常好的。它对于多线程的情况也是非常好的,当你经过这个应用程序的时候,这个软件瑕疵就消失了,但是,在正常速度运行时,这个软件瑕疵又出现了。

  默认情况下,IntelliTrace仅仅收集IntelliTrace相关的事件,这样是为了保持所收集的数据较少,并且对于性能上的影响是最小的。而当你选择对方法调用的数据也进行搜集时,除了其临时搜集的数据所占用空间的增大外,对于性能的影响也是较大的。尽管收集到的细节有所不同,但两种方式都会收集一些共同的数据。比如,它总会在第一次启动收集过程时收集系统信息,模块的加载和卸载事件,线程的起始和结束时间等。随着模块和线程的事件,可以正确的更新模块和线程调试窗口的信息。另外,任何模式下,在调试断点处也会对数据进行收集,并将所收集到的基本数据类型和对象在调试其中进行检查和审阅,并允许改变其值。

  总的说来IntelliTrace调试程序就是为程序开发者量身定制的,通过其独特的“历史调试”功能,为开发者提供了一个反悔追溯过程控制功能。比如,在开发中程序在10分钟前出现一个异常,怎么办呢,如果在以前一般是通过手动调用Debug.Write语句,现在不必了!因为不少开发工具中的这种历史调试功能都非常相似,在VS2010 正式版 中通过IntelliTrace,不但能使程序员看到程序的当前状况,也可以检查某些情况下产生的事件,甚至还能隔离应用程序历史记录中的故障点。所以,启用历史调试之后,VS2010 正式版能够捕获调用堆栈以及相关变量,咱们就可以“回退”至程序的保存前10分钟或者更长时间,检查程序发生异常、故障时的各种情况。因为,在默认情况下程序只保存关键的类型,而开发人员可以选择记录所有的参数,对象以及全局变量。所以,IntelliTrace会主动展示程序运行过程中的关键事件,诸如代码异常、文件访问、时时调试等等。

  更好的理解使用IntelliTrace调试功能,向你推荐MSDN杂志上上的一篇文章IntelliTrace: 使用 IntelliTrace 调试应用程序,Justin Marks 演示了如何使用 Visual Studio 2010 旗舰版中提供的 IntelliTrace 功能,该功能为开发人员提供了一种强大的新调试工具来加速调试并收集应用程序的完整执行历史记录、获得更深入的信息以及快速查找 Bug。

十招教你提高ASP.NET页面载入速度

2010年05月8日 8:38 上午  |  分类:.Net技术

        本文是我对ASP.NET页面载入速度提高的一些做法,这些做法分为以下部分:

  1.采用 HTTP Module 控制页面的生命周期。

  2.自定义Response.Filter得到输出流stream生成动态页面的静态内容(磁盘缓存)。

  3.页面GZIP压缩。

  4.OutputCache 编程方式输出页面缓存。

  5.删除页面空白字符串。(类似Google)

  6.完全删除ViewState。

  7.删除服务器控件生成的垃圾NamingContainer。

  8.使用计划任务按时生成页面。(本文不包含该做法的实现)

  9.JS,CSS压缩、合并、缓存,图片缓存。(限于文章篇幅,本文不包含该做法的实现)

  10.缓存破坏。(不包含第9做法的实现)

  针对上述做法,我们首先需要一个 HTTP 模块,它是整个页面流程的入口和核心。

  一、自定义Response.Filter得到输出流stream生成动态页面的静态内容(磁盘缓存)

  如下的代码我们可以看出,我们以 request.RawUrl 为缓存基础,因为它可以包含任意的QueryString变量,然后我们用MD5加密RawUrl 得到服务器本地文件名的变量,再实例化一个FileInfo操作该文件,如果文件最后一次生成时间小于7天,我们就使用.Net2.0新增的TransmitFile方法将存储文件的静态内容发送到浏览器。如果文件不存在,我们就操作 response.Filter 得到的 Stream 传递给 CommonFilter 类,并利用FileStream写入动态页面的内容到静态文件中。

namespace  ASPNET_CL.Code.HttpModules {
    
public  class  CommonModule : IHttpModule {
        
public  void  Init( HttpApplication application ) {
            application.BeginRequest
+= Application_BeginRequest;
        }

         private  void  Application_BeginRequest(  object  sender, EventArgs e ) {
            var context
= HttpContext.Current;
            var request
= context.Request;
            var url
= request.RawUrl;

            var response = context.Response;
            var path
= GetPath( url );
            var file
=  new  FileInfo( path );

             if  ( DateTime.Now.Subtract( file.LastWriteTime ).TotalDays < 7 ) {
                response.TransmitFile( path );
                response.End();
                 return;
            }
             try  {
                var stream
= file.OpenWrite();
                response.Filter
=  new  CommonFilter( response.Filter, stream );
            }
             catch  ( Exception ) {
                
//Log.Insert(“”);
            }
        }

         public  void  Dispose() {

        }

         private  static  string  GetPath(  string  url ) {
            var hash
= Hash( url );
            
string  fold = HttpContext.Current.Server.MapPath(  ~/Temp/  );
             return  
string.Concat( fold, hash );
        }

         private  static  string  Hash(  string  url ) {
            url
= url.ToUpperInvariant();
            var md5
=  new  System.Security.Cryptography.MD5CryptoServiceProvider();
            var bs
= md5.ComputeHash( Encoding.ASCII.GetBytes( url ) );
            var s
=  new  StringBuilder();
             foreach  ( var b  in  bs ) {
                s.Append( b.ToString(  
x2  ).ToLower() );
            }
             return  s.ToString();
        }
    }
}

利用F#在Silverlight中实现并行编程

2010年05月8日 8:36 上午  |  分类:.Net技术

        随着.NET 4的发布,要在.NET平台之上进行并行编程可谓是易如反掌。实际上,微软为大家提供了三种并行编程的基础功能。第一种是提供给C++使用的并发运行时(Concurrency Runtime);第二种是藉由.NET平台提供给托管语言的一组并行编程函数库(包括TPL、PLINQ和并行编程数据结构等);最后一种就是基于F#本身的并行编程特性。

  然而,由于考虑精简的缘故,当前的CoreCLR并未包含.NET 4中的并行函数库,从而在Silverlight(即使最新的Silverlight 4)中也无法直接获得并行编程的能力。但是,大家不用气馁,我们还可以在Silverlight中利用F#来实现并行编程。

  Bart Czernicki在其博文《Silverlight 3 and F# Support in Visual Studio 2010》中详细讲述了这种方式。在这篇博文中,Bart给出利用F#进行并行编程的具体步骤,相应的示例代码,以及对性能的讨论;还表达了一个非常有意思的观点:

  并发编程是Silverlight超越其他RIA技术的最大特性。

  Bart也提到,使用F#来在Silverlight中辅助进行并行编程的好处:

  不用显式地启动、停止线程(比如调用Thread.Start)

  不需使用BackgroundWorker

  不需使用回调(AsyncCallBack)

  无需遵循BeginExecute这样的异步模式

  当然,通过F#来在Silverlight中实现并行编程,还是有诸多的不足:

  不能利用PLINQ

  如果你的代码主要由C#、VB写成,那么只能用F#编写一个包装器,来变相地实现并行

  目前这种方式只能在Silverlight 3上使用,这是由于F# runtime for Silverlight只发布了Silverlight 3的版本,而Silverlight 4版本的F#运行时要在Silverlight 4 Tools for VS2010正式发布的时候才会提供。

  对于上面的最后一点,Gaston Hillar的博文《Silverlight 4 RC Stays With the Old .NET Threads; F# Helps》也有所提及。

  无论如何,虽然我们现在可以变通地在Silverlight中进行并行编程,但还是希望微软能在未来的Silverlight版本中提供直接的并行编程功能。

.NET技术教你设计CMS数据库

2010年04月30日 8:32 上午  |  分类:.Net技术

博客的这种形式就不大好解决这种需求了,当然也许是我对博客还不了解,没有用好吧。所以我想做一个网站,这个网站专门介绍自然框架。一开始只想做一个静态的,内容也不多嘛,做几个页面,介绍一下,把博客里的随笔整理一下做个目录便于阅读。但是试了一下才发现,静态页面好麻烦呀,也许是我太懒了吧,总是想简单一些。于是就想做一个简单的CMS,然后用这个CMS来做自然框架的介绍网站。

  您可能会说了,海洋又在重复制造轮子了,网上有一大堆现成的,有很多成熟的不去用,自己写什么呀?

  首先呢,我是程序员(嘿嘿),我先想到的是我自己能不能做出来?别人能做我为什么不行?我不是顾客,我也不是有钱人,到处去弄现成的。其次呢,做一个CMS也是一个练手的机会,同时也是自然框架的一个Demo,比较大的、完整的Demo。借此来说明自然框架的使用方式,和在网页里的作用。最后就是想借此说一下我的设计数据库的思路。我觉得我的设计数据库的思路还是有点特色的。

  好了,开始进入正题。

  首先是了解需求。一个网站会有什么?首页、新闻(图文形式的信息)、产品介绍、文件下载、图片浏览、在线视频等。这些都算是“内容”的几种形式吧,当然还可以有其他的形式。

  这个需求比较简单,也比较简陋,暂时就以这个需求来进行设计吧。如果是按照面向对象的方式要如何设计呢?这个我不太清楚,也许是要画一个UML吧,也许要建模。尝试一下,画了一个UML不知道对不对,拿出来请大家批批。

  【CMS的类图】

  图很简单也没什么具体的属性,因为需求是变化的,现在也没有太具体的需求,所以属性就先设置几个主要的。另外俺英文不好,怕查出来的英文单词不正确产生歧义,所以直接用汉字了。可能您看着很别扭,但是至少不会产生什么歧义,理解起来也会比较容易吧,呵呵。

  “内容”作为父类,其他的作为子类。内容是一种“抽象”,把各种形式的内容的共同部分提炼出来,比如标题、内容、添加人、添加日期、点击量等。子类负责各自特有的属性。

  我觉得这种提炼的方式比较好,在设计数据库表结构的时候可以借鉴一下。于是就有了这样的数据库设计。

  【CMS ER图】

  “内容”作为主体和中心,其他的都是为了这个中心(内容)来服务的。左面是对内容的限制,栏目相当于大分类,分类就是小分类(可以是n级的),类型就是内容的形式,比如图文、下载、视频、图片等。右面是扩展。扩展和类型是一一对应的。

  这就形成了一个“骨架”,骨架是以“内容”为中心,ArticleID作为关联字段,可以增加扩展表,但是都要以ArticleID作为关联字段。至于有多少扩展表,那就可以根据实际需求来变化,表里的字段也是可以根据需求来增减。

  设置这种“骨架”的好处:虽然扩展表、字段会有变化,但是“骨架”结构是不变的。这样一是可以让结构清晰,抓住中心、重点;二是当需求变化的时候,对结构的影响降到最低;三是,如果对于这种“骨架”习惯、掌握了之后,在看到其他项目的设计就会很容易进入和读懂。关于第三点,以后大家就会理解的。

  基本思路就是这样,抛砖引玉了。

        ps:CMS的字段说明

.NET平台下一个小Web开发项目总结

2010年04月30日 8:30 上午  |  分类:.Net技术

一直以来很少写Web开发的具体代码,以前是专门有人写这些,我只管管就行了。后来呢,也很少做Web类型项目了,最近有两个项目,牵扯到Web开发,而现在一个人单干,也找不到人来写这些了,只得自己憋手蹩脚的摸索。下面是这段时间的摸索总结。

  一、代码生成器确实好用

  以前是很反代码生成器的,但真的使用过后觉得很好用——简单、直接且控制力强。我用的是园子里李天平(http://ltp.cnblogs.com/)的动软代码生成器。在此,先感谢一下。不过,在感谢李天平之前,我必须先感谢一下郭嘉。

  动软的好处是简单、直接、开源。针对我的应用,我做了以下改进:

  (1)Image的处理问题。代码生成器生成的操作Image部分的代码有小问题,不能插入全部的Image对象,这个手动修改了下。

  (2)生成的代码没有 partial 关键字,不方便扩展。我修改成产生的所有类都是partial类。这样做有什么好处呢?就是对于每个类,你可以在不修改生成的源文件的情况下,申明某个类实现了某些接口,以对代码进行复用。

  例如,假设Album、News、Knowledge这三个类都具有某些相同的属性(ID,Title,AccountId,ViewCount,CreateTime,UpdateTime,等),在代码生成器生成的文件之外,另建一个目录,放一个Interface.cs的文件:

public interface IContent { Guid Id { get; set; } String Title { get; set; } String AccountId { get; } String Caption { get; } Int32 ViewCount { get; set; } DateTime CreateTime { get; set; } DateTime UpdateTime { get; set; } } public interface IContentVoteable : IContent { Int32 VoteCount { get; set; } } public partial class Album : IContent { public String Caption { get { return 相册; } } } public partial class News : IContent { public String Caption { get { return 资讯; } } } public partial class Knowledge : IContent { public String Caption { get { return 手记; } } }

这样就可以对生成的类进行类型“指派”,可用于泛型代码来简化编程。并且这种“指派”是强类型的,可在编译器检查错误的。

  二、分页

  这是一个老话题了。以前我直接用GridView和ObjectDataSource,结果是当需求变化了痛苦不堪。比如,客户要求用 VS2005 ……

  本着拿来主义的原则,拿来了园子里张子阳(http://www.cnblogs.com/jimmyzhang/)的分页代码。感谢张子阳。在感谢张子阳之前,再感谢一下郭嘉。

  具体的开发过程中做了一些思考,做出了以下改变:

  (1)没有使用分页存储过程,而是在C#代码中进行封装,纳入我的基础库里面。这样做的理由是:

  (a)分页代码比较简单,分页的数据库操作比较耗时,作为存储过程对性能提高不明显;

  (b)存储过程使用前需要添加进数据库,而在C#端使用则可以省略这一步骤。

  (c)方便调用、修改和封装。以下是我写的C#端的数据库分页代码:

  代码

public static DataTable GetPagerData(String tableName, String returnColumns, String where, String orderColumnName, Boolean orderDesc, String keyColumnName, Int32 pageSize, Int32 pageIndex, params SqlParameter[] whereParams) { if (pageIndex == 1) { return GetTopData(pageSize, tableName, returnColumns, where, orderColumnName, orderDesc, RemoveNull(whereParams)); } else { String sql = String.Format(select top {0} {1} from {2} where {3} and {6} not in ( select top {7} {6} from {2} where {3} order by {4} {5} ) order by {4} {5}, pageSize, returnColumns, tableName, where, orderColumnName, orderDesc == true ? desc : String.Empty, keyColumnName, pageSize * (pageIndex - 1)); return Query(sql, RemoveNull(whereParams)).Tables[0]; } } public static Int32 GetDataCount(String tableName, String where, params SqlParameter[] whereParams) { String sql = String.Format(select count(*) from {0} where {1} , tableName, where); Int32 result = (Int32)GetSingle(sql, RemoveNull(whereParams)); return result; } public static DataTable GetTopData(Int32 top, String tableName, String returnColumns, String where, String orderColumnName, Boolean orderDesc, params SqlParameter[] whereParams) { String sql = String.Format(select top {0} {1} from {2} where {3} order by {4} {5}, top, returnColumns, tableName, where, orderColumnName, orderDesc == true ? desc : String.Empty); return Query(sql, whereParams).Tables[0]; } private static SqlParameter[] RemoveNull(SqlParameter[] whereParams) { List list = new List(); foreach (SqlParameter sq in whereParams) { if (sq != null) list.Add(sq); } return list.ToArray(); }

在这段代码中,我直接将分页和Top集成在一起了,取第一页时,使用的是Top,以优化性能。同时,也省掉Top调用。这样做的好处,在下面能体现出来。

  (2)对于分页部分的代码,我默认进行一下处理:

  (a)url的page参数指现在的pageIndex;

  (b)url的count参数指查询结果的总数量;

  “count” 和 “page ”也可以指定为其它字符串。当是第一页时,去调用 GetDataCount 方法,获得count参数,这个参数直接传递给第二页,第三页……的url,避免查看第二页第三页时,重复获得count,影响性能。

  三、封装和复用

  通过上面的处理,就很方便对代码进行封装和复用了。

  还是以上面的Album,News和Knowledge来说,这三个,编辑都可以从后台加精、推荐、设为热点、锁定、审核通过与否等操作。如何用最简单的代码来实现前台和后台所需要的以下需求呢:

  (1)获得全部加精的对象;获得N天内加精的对象;以上可以按不同的排序方式排序;获得审核通过的对象,获得N天内审核通过的对象;

  (2)获得全部推荐的对象;获得N天内推荐的对象;以上可以按不同的排序方式排序;获得审核通过的对象,获得N天内审核通过的对象;

  (3)获得全部热点的对象;获得N天内热点的对象;以上可以按不同的排序方式排序;获得审核通过的对象,获得N天内审核通过的对象;

  (4)获得最新的对象;获得N天内审核通过的对象;

  (5)获得浏览量最多的对象;获得N天内浏览量最多的对象;获得审核通过的对象,获得N天内审核通过的对象;

  (6)以上检索可指定帐号,也可以是全体帐号。

  ……

  这里假设Album,News和Knowledge在数据库里相关列的列名都是一致的,EnableStatus代表审核通过与否,0代表待审核,1代表审核通过,-1代表审核未通过;IsLocked,IsHot,IsChoiced,IsMarked等编辑的操作项,分别代表是否锁定,是否热门,是否推荐和是否精华。News和Knowledge还有个CategoryId的列,代表所属类别。

  以News为例子,代码为:

  代码

  就只用这简简单单的代码就可以了。这里各个查询选项是“正交”的。accountId 为 null或Empty,则检索全部。mark代表编辑的查询类型,是全部,还是精华还是热门还是最新……,实际上这里最好是传入enum。mode代表审核状态,最好使用enum。这两处没用enum是历史遗留问题。categoryId代表类别id,如果为负数则检索全部的类别,inDays代表检索多少天内的数据,负数代表全部,orderColumnName 是排序列,我默认全部desc了,pageSize 是页大小,pageIndex是页序号。

public partial class NewsService : Orc.HairFashion.BLL.News { public static NewsService Instance; static NewsService() { Instance = new NewsService(); } public static void UpdateColumnStatus(ICollection ids, String columName, int status) { DbHelper.UpdateColumnStatus(ids, News, columName, status); } #region 分页查找 protected static DataTable GetPagerData(String where, String orderColumnName, Int32 pageSize, Int32 pageIndex, params SqlParameter[] whereParams) { return DbHelper.GetPagerData(”News”, “*”, where, orderColumnName, true, “Id”, pageSize, pageIndex, whereParams); } protected static Int32 GetDataCount(String where, params SqlParameter[] whereParams) { return DbHelper.GetDataCount(”News”, where, whereParams); } public static List SelectData(String accountId, String mark, String mode, Int32 categoryId, Int32 inDays, String orderColumnName, Int32 pageSize, Int32 pageIndex) { DataTable table = GetPagerData(” 1=1 ” + Util.BuildSqlByMark(mark) + Util.BuildSqlByMode(mode) + Util.BuildSqlByCategoryId(categoryId) + Util.BuildSqlByCreateInDays(inDays) + Util.BuildSqlByAccountId(accountId), orderColumnName, pageSize, pageIndex, Util.BuildSqlParameterByAccountId(accountId)); return NewsService.Instance.DataTableToList(table); } public static Int32 SelectCount(String accountId, String mark, String mode, Int32 categoryId, Int32 inDays) { return GetDataCount(” 1=1 ” + Util.BuildSqlByMark(mark) + Util.BuildSqlByMode(mode) + Util.BuildSqlByCategoryId(categoryId) + Util.BuildSqlByCreateInDays(inDays) + Util.BuildSqlByAccountId(accountId), Util.BuildSqlParameterByAccountId(accountId)); } #endregion }

这部分代码是在我动软代码生成器的源代码,使它生成带partial的类之前修改的。如果现在写,使用泛型的话,可以将上面的代码进一步简化。

BaseListControl public abstract class BaseListControl : System.Web.UI.UserControl { private Boolean m_showPager = false; private Int32 m_pageSize = 12; private Boolean m_orderByVoteCount = true; private Int32 m_inDays = 7; private String m_caption = “”; private String m_viewCaption = 点击; private String m_voteCaption = 票数; private String m_orderBy = CreateTime; private String m_mark = “”; private String m_mode = “”; public Int32 Count = 0; public Boolean ShowPager { get { return m_showPager; } set { m_showPager = value; } } public Int32 PageSize { get { return m_pageSize; } set { m_pageSize = value; } } public String Caption { get { return m_caption; } set { m_caption = value; } } public String ViewCaption { get { return m_viewCaption; } set { m_viewCaption = value; } } public String VoteCaption { get { return m_voteCaption; } set { m_voteCaption = value; } } public String OrderBy { get { return m_orderBy; } set { m_orderBy = value; } } public String Mark { get { return m_mark; } set { m_mark = value; } } public String Mode { get { return m_mode; } set { m_mode = value; } } public Int32 InDays { get { return m_inDays; } set { m_inDays = value; } } protected void Page_Load(object sender, EventArgs e) { if (Page.IsPostBack == false) { OnPageLoad(); InitPager(); BindData(); } } protected abstract PagerControl GetPager(); protected abstract Int32 GetResultCount(); protected virtual void OnPageLoad() { } protected void InitPager() { PagerControl pager = GetPager(); if (ShowPager == false) { if(pager!=null) pager.Visible = false; } else { Int32 count = PageUtil.GetCurrentCount(this.Page); if (count < 0) count = GetResultCount(); if(pager!=null) pager.UrlManager = new Orc.Util.AspDotNet.DefaultUrlManager(count, PageSize, page, count); } } protected void BindData() { Int32 page = PageUtil.GetCurrentPage(this.Page); if (page < 1) page = 1; BindData(page); } protected abstract void BindData(Int32 page); }

然后,写一个公用的控件类,以复用共有代码:

  然后是具体的控件:

Controls_NewsList public partial class Controls_NewsList : BaseListControl { private Int32 m_categoryId = -1; public Int32 CategoryId { get { return m_categoryId; } set { m_categoryId = value; } } protected override Orc.Util.AspDotNet.PagerControl GetPager() { return this.pager; } protected override int GetResultCount() { return NewsService.SelectCount(null, this.Mark, this.Mode, this.CategoryId, this.InDays); } protected override void BindData(int page) { this.rpList.DataSource = NewsService.SelectData(null, this.Mark, this.Mode, this.CategoryId, this.InDays, this.OrderBy, this.PageSize, page); this.rpList.DataBind(); } }

 这样一封装就超级好用,可以显示pager也可以不显示,可以用在分页里,也可以用在栏目的首页,且代码量几乎降低到最低,而性能几乎提高到最高,还可以根据各种参数进行缓存。

  四、抱怨

  尽管使用了这些技巧,Web开发还是太累。一个月坐下来还没做其它项目三五天赚的钱多,并且技术支持比其它类型项目难得多。做一个Web项目后悔一次,今后,尽量不做了,除非价格很高或自己用。

浅尝.NET 4并行计算 效率已大幅提升

2010年04月26日 7:48 上午  |  分类:.Net技术

随着Visual Studio 2010正式版的发布,我们已经可以用上.NET 4的所有功能。那么对于并行计算的尝试,是本文的重点。  

         我们都知道CPU的性能至关重要,但主频已经越来越难以提升,纵向发展受限的情况下,横向发展成为必然——核心数开始越来越多。然而多核心的利用、并行计算一直是编程中的难题,大的不说,就说代码的编写,程序员大多都有过痛苦的经历:多线程的程序代码量大,编写复杂,容易出错,并且实际运行效率是否理想也较难保证。

  为改善这种状况,.NET 4.0中引入了 TPL(任务并行库),关于TPL,MSDN的简介是:

  任务并行库 (TPL) 的设计是为了能更简单地编写可自动使用多处理器的托管代码。使用该库,您可以非常方便地用现有序列代码表达潜在并行性,这样序列代码中公开的并行任务将会在所有可用的处理器上同时运行。通常这会大大提高速度。

  简而言之,TPL提供了一系列的类库,可以使编写并行运算的代码更简单和方便。

  说起来很简单,我们来看点例子:

void ThreadpoolMatrixMult(int size, double[,] m1, double[,] m2, double[,] result) { int N = size; int P = 2 * Environment.ProcessorCount; // assume twice the procs for // good work distribution int Chunk = N / P; // size of a work chunk AutoResetEvent signal = new AutoResetEvent(false); int counter = P; // use a counter to reduce // kernel transitions for (int c = 0; c < P; c++) { // for each chunk ThreadPool.QueueUserWorkItem(delegate(Object o) { int lc = (int)o; for (int i = lc * Chunk; // iterate through a work chunk i < (lc + 1 == P ? N : (lc + 1) * Chunk); // respect upper // bound i++) { // original inner loop body for (int j = 0; j < size; j++) { result[i, j] = 0; for (int k = 0; k < size; k++) { result[i, j] += m1[i, k] * m2[k, j]; } } } if (Interlocked.Decrement(ref counter) == 0) { // use efficient // interlocked // instructions signal.Set(); // and kernel transition only when done } }, c); } signal.WaitOne(); }

很眼熟但同时看着也很心烦的代码吧。在换用TPL后,上面的代码就可以简化为:

void ParMatrixMult(int size, double[,] m1, double[,] m2, double[,] result) { Parallel.For( 0, size, delegate(int i) { for (int j = 0; j < size; j++) { result[i, j] = 0; for (int k = 0; k < size; k++) { result[i, j] += m1[i, k] * m2[k, j]; } } }); }

  舒服多了吧?具体的内容请见MSDN的文章 优化多核计算机的托管代码。

  装好正式版的VS2010以后,写了段代码来测试下,TPL究竟好不好用。

  代码很简单,拿一条字符串和一堆字符串里的每一条分别用LevenshteinDistance算法做字符串相似程度比对。先用传统的顺序执行的代码跑一遍,记录下时间;再换用TPL的并行代码跑一遍,记录下时间。然后比对两次运行的时间差异。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace ParallelLevenshteinDistance { class Program { static void Main(string[] args) { Stopwatch sw; int length; int count; string[] strlist; int[] steps; string comparestring; Console.WriteLine(Input string lenth:); length = int.Parse(Console.ReadLine()); Console.WriteLine(Input string list count:); count = int.Parse(Console.ReadLine()); comparestring = GenerateRandomString(length); strlist = new string[count]; steps = new int[count]; // prepare string[] for comparison Parallel.For(0, count, delegate(int i) { strlist[i] = GenerateRandomString(length); }); Console.WriteLine({0}Computing…{0}, Environment.NewLine); // sequential comparison sw = Stopwatch.StartNew(); for (int i = 0; i < count; i++) { steps[i] = LevenshteinDistance(comparestring, strlist[i]); } sw.Stop(); Console.WriteLine([Sequential] Elapsed:); Console.WriteLine(sw.Elapsed.ToString()); // parallel comparison sw = Stopwatch.StartNew(); Parallel.For(0, count, delegate(int i) { steps[i] = LevenshteinDistance(comparestring, strlist[i]); }); sw.Stop(); Console.WriteLine([Parallel] Elapsed:); Console.WriteLine(sw.Elapsed.ToString()); Console.ReadLine(); } private static string GenerateRandomString(int length) { Random r = new Random((int)DateTime.Now.Ticks); StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; i++) { int c = r.Next(97, 123); sb.Append(Char.ConvertFromUtf32(c)); } return sb.ToString(); } private static int LevenshteinDistance(string str1, string str2) { int[,] scratchDistanceMatrix = new int[str1.Length + 1, str2.Length + 1]; // distance matrix contains one extra row and column for the seed values for (int i = 0; i <= str1.Length; i++) scratchDistanceMatrix[i, 0] = i; for (int j = 0; j <= str2.Length; j++) scratchDistanceMatrix[0, j] = j; for (int i = 1; i <= str1.Length; i++) { int str1Index = i - 1; for (int j = 1; j <= str2.Length; j++) { int str2Index = j - 1; var cost = (str1[str1Index] == str2[str2Index]) ? 0 : 1; int deletion = (i == 0) ? 1 : scratchDistanceMatrix[i - 1, j] + 1; int insertion = (j == 0) ? 1 : scratchDistanceMatrix[i, j - 1] + 1; int substitution = (i == 0 || j == 0) ? cost : scratchDistanceMatrix[i - 1, j - 1] + cost; scratchDistanceMatrix[i, j] = Math.Min(Math.Min(deletion, insertion), substitution); // Check for Transposition if (i > 1 && j > 1 && (str1[str1Index] == str2[str2Index - 1]) && (str1[str1Index - 1] == str2[str2Index])) { scratchDistanceMatrix[i, j] = Math.Min(scratchDistanceMatrix[i, j], scratchDistanceMatrix[i - 2, j - 2] + cost); } } } // Levenshtein distance is the bottom right element return scratchDistanceMatrix[str1.Length, str2.Length]; } } }

这里只用了最简单的 Parallel.For 方法,代码很简单和随意,但是看看效果还是可以的。

  测试机找了不少,喜欢硬件的朋友兴许也能找到你感兴趣的:P

  Intel Core i7 920 (4物理核心8逻辑核心,2.66G) + DDR3 1600 @ 7-7-7-24

  AMD Athlon II X4 630 (4物理核心,2.8G) + DDR3 1600 @ 8-8-8-24

  AMD Athlon II X2 240 (2物理核心,2.8G) + DDR2 667

  Intel Core E5300 (2物理核心,2.33G) + DDR2 667

  Intel Atom N270 (1物理核心2逻辑核心,1.6G) + DDR2 667

  还在VM workstation里跑过,分别VM了XP和WIN7,都跑在上述i7的机器里,各自分配了2个核心。

  程序设置每个字符串长1000个字符,共1000条字符串。

  每个机器上程序都跑了3遍,取平均成绩,得到下表:

  CPU Core Time_Sequential(s) Time_Parallel(s) S/P(%)

  Intel Core i7 920 4 Cores, 8 Threads, 2.6G 55.132634 14.645687 376.44%

  AMD AthlonII X4 630 4 Cores, 4 Threads, 2.8G 58.10592 17.152494 338.76%

  AMD AthlonII X2 240 2 Cores, 2 Threads, 2.8G 66.159735 32.293972 204.87%

  Intel E5300 2 Cores, 2 Threads, 2.3G 70.827157 38.50654 183.94%

  Intel Atom N270 1 Cores, 2 Threads, 1.6G 208.47852 157.27869 132.55%

  VMWin7(2 logic core) 2 Cores, 2 Threads 56.965068 33.069084 172.26%

  VMXP(2 logic core) 2 Cores, 2 Threads 59.799399 35.35805 169.13%

  可见,在多核心处理器上,并行计算的执行速度都得到了大幅提升,即便是在单核心超线程出2个逻辑核的Atom N270上亦缩短了32.55%的运行时间。在A240上并行计算的效率竟然是顺序计算的204.87% ?!而同样是4核心,i7 920在超线程的帮助下,并行执行效率提升明显高过A630。最后VM里的测试,是否也可以在某种程度上佐证在多核心的调度上,Win7要强过XP呢(纯猜测)?顺带可以看到,同样是i7的硬件环境,单线程宿主OS(Win7)里执行花费55.133秒,VM(Win7)里56.965秒,速度上有约3%的差异。

  

浅谈.NET互操作技术 重点托管代码

2010年04月26日 7:46 上午  |  分类:.Net技术

多年来在程序设计领域微软技术一直引领着每个时代,在每一代的技术背后都有相关的动机,而这些动机及其实现细节往往是大部分的程序设计课程没有涉及的,但是对于我们理解相关的技术又十分重要,本文将对几种相关技术和隐藏在动机背后的细节做简要的阐述,算是抛砖引玉。

  COM技术于1993年首次发布,称得上是微软技术的一个里程碑,其意义在于使组件编程化得以实现。COM技术最初的思想起源于将类做成可重用的二进制组件,把类的实现和接口分离以便把类的实现封装到二进制防火墙的背后,而这道防火墙以VPTR和VTBL的形式保证了这个防火墙的不变性。.NET技术则在语言层面上支持了基于组件的程序设计。相对于COM技术,.NET技术就是为基于组件的程序设计而生的,所以其编码效率和语言可读性要高出COM一个等级。但是由于1993年以后的十多年时间里,人们利用基于COM的组件程序设计技术开发了大量的高复杂度,高可用性的代码,并且他们运行良好,效率极高,如果用.NET技术重写这些代码简直就是得不偿失,加之在.NET的实现过程中并不是所有的Windows API都被重写,有些功能是.NET所不能完成的。面对这样的问题, .NET互操作技术应运而生,可见需求才是技术的原动力。

  .NET互操作技术主要分为3种,P/Invoke,C++ Interop,COM Interop,其中P/Invoke 主要用于调用C库函数和Windows API。C++ Interop则主要用于Managed C++调用 C++类库和核心算法库,它甚至允许托管代码和非托管代码在同一个文件中。 COM Interop主要包括正向的RCW和反向的CCW。下面以一个简单的例子对互操作中比较重要的数据封送进行简单的介绍。

  如何封送字符串(P/Invoke方式)

  假设非托管代码定义如下:

void _cdecl stringMarshal( const wchar_t* inString, wchar_t* outString, int buffersize) { If(NULL != inString) { wcscpy_s(outString, buffersize, inString); } }

这段代码编译生成的文件名为:stringMarshal.dll

  在托管代码中其托管定义如下:

[DllImport (“stringMarshal.dll”, CharSetCharSet = CharSet.Unicode, CallingConventionCallingConvention =CallingConvention.Cdecl) ] public extern static void stringMarshal (string inString, StringBuilder outString, int bufferSize);

    这里有几点需要注意:

  1. 在声明函数时必须要用extern修饰符,目的是为了告诉编译器此函数是外部实现的,没有方法体,因此不需要在托管代码中搜索这个函数。

  2. 在声明函数时必须要用static修饰符,原因是非托管的DLL导出的非托管方法都是可以直接调用的,无需对相关的类进行实例化,大部分情况下根本就不存在类。

  3. 因为非托管代码中的字符串为wchar_t*类型,所以CharSet需要设置为CharSet.Unicode。

  4. 因为非托管代码的调用方式为_cdecl, 所以托管部分的CallingConvention需要设置为CallingConvention.Cdecl,另外这种类型的调用方式是调用方负责处理堆栈,所以支持可变类型参数函数例如printf()的互操作。

  5. 输入字符串需要封装为string是因为这个字符串属于固定字符串,互操作过程中不需要变化,而输出字符串则需要封装为StringBuilder,因为这种字符串默认为具有IN/OUT属性,其内容可变,而且当字符串经常需要变化时效率高。

  托管代码中调用非托管代码方式如下:

private static void TestStringMarshal () { string inString = Wally input test string.; int bufferSize = inString.Length; StringBuilder strbd = new StringBuilder(bufferSize); stringMarshal (inString, strbd, bufferSize + 1); Console.WriteLine(Wally Input string: {0}, inString); Console.WriteLine(Wally output string: {0}, strbd.ToString()); }
Pages: 1 2 3 Next