好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

FM并发编程练习:改写苏飞的C#多线程网站压力测试程序

FM并发编程练习:改写苏飞的C#多线程网站压力测试程序

为了展示如何使用FastMessenger编写并发程序,我会重写或改写一些其它网友的多线程程序,给愿意学习和了解FastMessenger的朋友提供更多直观的范例。一般来说我会对原来的程序进行一些技术分析,得出原作者的设计意图和实现手段,然后解释用FastMessenger来重写或改写会使用哪些设计,最后给出源程序。

这次我选择的是苏飞网友(本人在此表示感谢)的C#多线程网站压力测试程序,他的博客和相关博文地址如下:

苏飞—C#.Net

C#多线程|匿名委托传参数|测试您的网站能承受的压力|附源代码

C#多线程|匿名委托传参数|测试您的网站能承受的压力|附源代码--升级版

据他自己介绍,这个程序虽然有一些实用价值,但主要是作为一个多线程的演示程序来写的。请大家在继续往下阅读本文之前,先到上面的地址看看他的文章和源程序,因为我接着的分析是基于他的程序来展开的。

这个图是我对苏飞的程序的分析和归纳的结果,如有错误,请指正。这是一个GUI(Windows Form)程序,程序启动后并不开始测试,而是等用户输入一些参数并按了一个button后开始。在button的event函数里,存放了创建和启动新线程的主要逻辑。每个线程都对应表中的一行,并且每个线程在创建的时候就通过一个匿名函数获得了和该行对应的行号和其它完成网站测试所需要的所有参数。这样每个线程都能提供正确的行号和列名,通过UpDateDgv()函数来更新GUI。

除了参数不同外,所有线程的主体逻辑都是一样的,都是调用PingTask()。但PingTask()里的更新GUI的代码却不是由这些新创建的线程直接调用的,而是通过调用BeginInvoke来间接调用的。这属于Windows Form的特性,就不多解释了。

上面所有的代码都被写在同一个class TextFor里了,但却没有race condition。主要是因为测试逻辑本身就是独立的,互相直接没有要协调同步的地方。然后所有的参数都被封装在PingTask()里了,对外没有要求,也没有接受外部请求(比如立即停止)的需求。最后再加上线程和GUI之间只有简单数据(行号、列名、和string的值)的单向联系(或耦合),以及BeginInvoke又隔离了一次,所以基本没有什么线程安全的问题要考虑。

分析完毕,现在转到如何用FastMessenger来编程了。FastMessenger里的object叫做active object,就是活的object的意思,与SOA里的service类似,你一给个request或message,它就会执行相应的功能,所以叫做活的、独立的object。等一下看代码就知道了,和其它的object没有什么区别,你可以把它理解成“封装了线程的object”。对active object最直观的理解方法是把它拟人化,比如下图中的兰色和红色的小人图像。当你给一个人(比如你的同事)布置了一个任务后,他自己有手有脚有脑子,自己会完成这个任务。你不需要象上面的多线程程序那样,即作爹(建立线程)又作妈(安排执行PingTask()函数),出了事还得善后(exception handling)。

所以我设计的时候,安排了一个角色叫SiteTester(图中下面那排兰色小人)。根据原来程序的结构,每个SiteTester负责表中的一行。由button的event函数负责建立(一人建立一次就可以了)SiteTester,并给每个SiteTester发测试请求,和原来的程序一样,每个请求里包含了测试所需要的所有参数。然后在表的那边(图中上部)安排了一个角色叫ViewUpdater,就一个人,负责接收所有SiteTester的更新GUI的请求。

有了上面对原来的程序和改写的程序的分析,下面列出的源程序,就不再逐行解释了,因为对应关系已经很明细了。

View Code

   1   using   System;
    2   using   System.Collections.Generic;
    3   using   System.ComponentModel;
    4   using   System.Data;
    5   using   System.Drawing;
    6   using   System.Text;
    7   using   System.Windows.Forms;
    8   using   System.Net;
    9   using   System.Threading;
   10   using   System.Data.SqlClient;
   11   using   Com.FastMessenger;
   12   using   Com.FastMessenger.Impl;
   13  
  14   namespace   AutoFor
   15   {
   16       public   partial   class   TextFor : Form
   17       {
   18           private   delegate   void  UpDateDgvDelegate( string  msg,  int  rowId,  string   columnName);
   19           private   UpDateDgvDelegate _upDateStateDelegate;
   20           private   IMessenger messenger;
   21           private   int  lastThreadCount =  0  ;
   22  
  23           public   TextFor()
   24           {
   25               InitializeComponent();
   26              _upDateStateDelegate =  new   UpDateDgvDelegate(UpDateDgv);
   27  
  28              messenger =  new   Messenger();
   29              messenger.RegisterReceiver( this ,  "  ViewUpdater  " ,  "  WebTestCompleted  "  );
   30           }
   31  
  32           private   void  UpDateDgv( string  msg,  int  rowId,  string   columnName)
   33           {
   34               try 
  35               {
   36                  dgvTextFor.Rows[rowId].Cells[columnName].Value =  msg.ToString();
   37               }
   38               catch   { }
   39           }
   40  
  41           public   void  WebTestCompleted( string  msg,  int  rowId,  string   columnName)
   42           {
   43               this  .BeginInvoke(_upDateStateDelegate, msg, rowId, columnName);
   44           }
   45  
  46           private   void  CreateTable( int   rows)
   47           {
   48              DataTable dt_Sale =  new   DataTable();
   49              DataColumn dc =  null  ;
   50               //  线程ID 
  51              dc =  new   DataColumn();
   52              dc.ColumnName =  "  线程ID  "  ;
   53              dc.DefaultValue =  "  1  "  ;
   54              dc.DataType = Type.GetType( "  System.String  "  );
   55               dt_Sale.Columns.Add(dc);
   56  
  57               //  循环类型 
  58              dc =  new   DataColumn();
   59              dc.ColumnName =  "  循环类型  "  ;
   60              dc.DefaultValue =  "   "  ;
   61              dc.DataType = Type.GetType( "  System.String  "  );
   62               dt_Sale.Columns.Add(dc);
   63  
  64               //  当前循环次数 
  65              dc =  new   DataColumn();
   66              dc.ColumnName =  "  当前循环次数  "  ;
   67              dc.DefaultValue =  "   "  ;
   68              dc.DataType = Type.GetType( "   System.String  "  );
   69               dt_Sale.Columns.Add(dc);
   70  
  71               //  开始时间 
  72              dc =  new   DataColumn();
   73              dc.ColumnName =  "  开始时间  "  ;
   74              dc.DefaultValue =  "   "  ;
   75              dc.DataType = Type.GetType( "  System.String  "  );
   76               dt_Sale.Columns.Add(dc);
   77  
  78               //  结束时间 
  79              dc =  new   DataColumn();
   80              dc.ColumnName =  "  结束时间  "  ;
   81              dc.DefaultValue =  "   "  ;
   82              dc.DataType = Type.GetType( "  System.String  "  );
   83               dt_Sale.Columns.Add(dc);
   84  
  85               //  总用时(毫秒) 
  86              dc =  new   DataColumn();
   87              dc.ColumnName =  "  总用时(毫秒)  "  ;
   88              dc.DefaultValue =  "   "  ;
   89              dc.DataType = Type.GetType( "  System.String  "  );
   90               dt_Sale.Columns.Add(dc);
   91  
  92               //  测试数据 
  93              dc =  new   DataColumn();
   94              dc.ColumnName =  "  数据  "  ;
   95              dc.DefaultValue =  "   "  ;
   96              dc.DataType = Type.GetType( "  System.String  "  );
   97               dt_Sale.Columns.Add(dc);
   98  
  99              DataRow dr =  dt_Sale.NewRow();
  100               for  ( int  i =  1 ; i < rows; i++ )
  101               {
  102                  dr[ "  线程ID  " ] =  i.ToString();
  103                  dr[ "  循环类型  " ] =  "  For循环  "  ;
  104                  dr[ "  当前循环次数  " ] =  "  0  "  ;
  105                  dr[ "  开始时间  " ] =  "  0  "  ;
  106                  dr[ "  结束时间  " ] =  "  0  "  ;
  107                  dr[ "  总用时(毫秒)  " ] =  "  0  "  ;
  108                  dr[ "  数据  " ] =  ""  ;
  109                   dt_Sale.Rows.Add(dr);
  110                  dr =  dt_Sale.NewRow();
  111               }
  112              dgvTextFor.DataSource =  dt_Sale;
  113           }
  114  
 115           private   void  button3_Click( object   sender, EventArgs e)
  116           {
  117               int  threadCount =  Convert.ToInt32(txtCount.Text.Trim());
  118               int  repeatCount =  Convert.ToInt32(txtNumber.Text.Trim());
  119               string  url =  textBox1.Text.Trim();
  120               int  repeatInterval =  Convert.ToInt32(txtForTime.Text.Trim());
  121  
 122              CreateTable(threadCount +  1  );
  123  
 124               //   when lastThreadCount > threadCount 
 125               for  ( int  i = threadCount; i < lastThreadCount; i++ )
  126               {
  127                  messenger.DeregisterReceiver( "  SiteTester-  "  +  i);
  128               }
  129  
 130               //   when lastThreadCount < threadCount 
 131               for  ( int  i = lastThreadCount; i < threadCount; i++ )
  132               {
  133                  SiteTester tester =  new  SiteTester(messenger,  "  ViewUpdater  " ,  "  WebTestCompleted  "  );
  134                  messenger.RegisterReceiver(tester,  "  SiteTester-  "  + i,  "  TestSite  "  );
  135               }
  136  
 137               for  ( int  i =  0 ; i < threadCount; i++ )
  138               {
  139                  messenger.SendMessage( "  SiteTester-  "  + i,  "  TestSite  "  , i, repeatCount, url, repeatInterval);
  140               }
  141  
 142              lastThreadCount =  threadCount;
  143           }
  144       }
  145  
 146       class   SiteTester
  147       {
  148           private   IMessenger messenger;
  149           private   string   returnId, returnPort;
  150  
 151           public  SiteTester(IMessenger messenger,  string  returnId,  string   returnPort)
  152           {
  153               this .messenger =  messenger;
  154               this .returnId =  returnId;
  155               this .returnPort =  returnPort;
  156           }
  157  
 158           public   void  TestSite( int  dgvrowid,  int  number,  string  url,  int   time)
  159           {
  160              DateTime st =  DateTime.Now;
  161  
 162              messenger.SendMessage(returnId, returnPort, st.ToString( "  hh-mm-ss  " ), dgvrowid,  "  开始时间  "  );
  163  
 164               for  ( int  i =  0 ; i < number; i++ )
  165               {
  166                   try 
 167                   {
  168                      DateTime stThread =  DateTime.Now;
  169  
 170                      HttpHelps hh =  new   HttpHelps();
  171  
 172                       string  strdate = hh.GetHttpRequestStringByNUll_Get(url,  null  );
  173                      DateTime et =  DateTime.Now;
  174  
 175                      messenger.SendMessage(returnId, returnPort, strdate, dgvrowid,  "  数据  "  );
  176                      messenger.SendMessage(returnId, returnPort, (i +  1 ).ToString(), dgvrowid,  "  当前循环次数  "  );
  177                      messenger.SendMessage(returnId, returnPort, et.ToString( "  hh-mm-ss  " ), dgvrowid,  "  结束时间  "  );
  178                      messenger.SendMessage(returnId, returnPort, ExecDateDiff(st, et), dgvrowid,  "  总用时(毫秒)  "  );
  179  
 180                       while  (stThread.AddSeconds(time) >  DateTime.Now)
  181                       {
  182                          Thread.Sleep( 200  );
  183                       }
  184                   }
  185                   catch   { }
  186               }
  187           }
  188  
 189           private   string   ExecDateDiff(DateTime dateBegin, DateTime dateEnd)
  190           {
  191              TimeSpan ts1 =  new   TimeSpan(dateBegin.Ticks);
  192              TimeSpan ts2 =  new   TimeSpan(dateEnd.Ticks);
  193              TimeSpan ts3 =  ts1.Subtract(ts2).Duration();
  194               return   ts3.TotalMilliseconds.ToString();
  195           }
  196       }
  197  }

为了节省版面,上面的源程序已经去掉了一些原来的过于明显的注释了。要运行的话,还需要FastMessenger的代码(要看了本文之后下载的,因为刚改了个不bug),请到这里( https://github测试数据/fastmessenger/RI-in-Csharp )下载。

如有任何问题,请留言,我会尽量解答。

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于FM并发编程练习:改写苏飞的C#多线程网站压力测试程序的详细内容...

  阅读:38次