在Web客户端触发多线程来提升应用程序的性能
在执行一个循环多次的重复操作任务时(例如向DB中一次性插入10万条数据,又如评标系统中导入数十家单位的投标文件),可能引起web服务器很长时间没有响应,客户端等待数十秒甚至更长时间才看到任务执行完毕。如果采用传统的编码方式,这些重复任务只能在Web服务器端程序中通过循环来完成,要提升性能只能通过改进每次循环的执行效率。
事实上,我们可以利用Web服务器的多线程能力来让这些重复任务“并行”地执行,从而极大地缩短原来“串行”执行的总时间。
操作方法
- 01
1、Web服务器的多线程能力 IIS7中的一个应用程序池是一个独立的进程,一个进程拥有一个线程池,应用程序池中可以有多个WebApplication,每个WebApplication运行在一个单独的AppDomain中,这些WebApplication公用一个线程池。不同的AppDomain保证了每个WebApplication的静态变量不会互相干扰,不同的应用程序池保证了一个网站瘫痪,其他不同进程中的站点还能正常运行,而线程池中的多个线程能够独立响应客户端的用户请求,这样Web服务器才具备了同时服务于很多用户的能力。我们就可以利用这个线程池来实现客户端触发的多线程任务。 下图说明了这几个对象之间的关系:
- 02
但是需要注意,asp.net中每个请求都会有有一个线程来处理,而可以使用的线程是有限的,服务器会使用一个线程池来管理线程,当线程耗尽,新来的请求只能蹲着排队,所以对web开发者而言,线程是个宝贵的资源,所以这个方案在并行处理的同时也增加了耗尽线程池的风险,所以要评估可以应用的场合。
- 03
2、异步调用 在Winform程序中,执行一个长耗时的任务时,可以通过异步调用的方法来处理,异步任务处理完成时,回调函数会CallBack通知主程序。在Asp.net中,也可以通过异步调用WebService的方式来实现类似要求。异步调用WebService是不需要在WebService中做任何操作的,只是普通的WebService就可以了,调用方则采用类似下面的代码实现异步调用: public _Default() { asynSer = new AsynWebService.Service1(); asynSer.HelloWorldCompleted += new AsynWebService.HelloWorldCompletedEventHandler(asynSer_HelloWorldCompleted); } protected void Button1_Click(object sender, EventArgs e) { //开始异步调用HelloWorld; asynSer.HelloWorldAsync(); } //回调函数 protected void asynSer_HelloWorldCompleted(object sender, AsynWebService.HelloWorldCompletedEventArgs e) { this.Label1.Text = e.Result.ToString(); }
- 04
3、采用Jquery实现WebService的异步调用 我们也可以在客户端使用Javascript脚本实现WebService的异步调用。参考代码如下: $("#Button1").click(function () { $.ajax({ type: "post", contentType: "application/json", url: "Service1.asmx/Insert_1500_Loop", data: "{loopCount:20}",//传给webService的参数 datatype: "json", success: function (result) { //回调函数,等待任务异步执行完成后返回 $('#result').append("任务执行完成"); } }); });
- 05
三、多线程提升程序性能的实现 下面我以向DBMail_AllMails表插入30000条数据为例,来说明客户端如何发起多线程以提升程序性能。 1、 WebService编写 //下面的服务完成1500条记录的插入 [WebMethod] public string Insert_1500(int taskNumber) { new DBOp().Insert_1500(); return taskNumber.ToString(); }
- 06
2、 Aspx网页中用Jquery发起多个请求,触发多任务 (1)按钮 <input id="Button1" type="button" value="开始多线程插入数据,计划触发20个任务,每个任务插入1500条记录" /> (2)Javascript脚本 <script type="text/javascript"> var activeThread = 0; var totalTasks = 20; var curThread = 0; var taskToDo = 20; //数据插入操作调用,每隔100ms启动一个线程,总线程数为10个。 function InvokeDBOp() { if (activeThread < 20 && curThread < totalTasks) { curThread++; activeThread++; $.ajax({ type: "post", contentType: "application/json", url: "Service1.asmx/Insert_1500", data: "{taskNumber:" + curThread + "}", datatype: "json", success: function (result) { $('#result').append("任务" + result.d + "已完成<br>"); activeThread--; taskToDo--; if (taskToDo == 0) { var dat = new Date(); $('#result').append("多线程任务结束时间:" + dat.toLocaleString() + "<br>"); } } }); } } //按钮的Click事件,每个5毫秒发起一个任务(触发web服务器的一个线程) $("#Button1").click(function () { var dat = new Date(); $('#result').append("多线程任务开始时间:" + dat.toLocaleString() + "<br>"); totalTasks = 20; taskToDo = 20; setInterval("InvokeDBOp()", 5); }); </script> 3、 性能比较 经过测试,多线程方式比传统方式起码提升了50%的性能。 另外,采用Jquery异步调用WebService的方式也可以实现“网页多线程(仇洋菁)”文中遇到的问题,并且还能在异步任务执行完成时通过回调函数告知调用主程序。