你的位置:首页 > ASP.net教程

[ASP.net教程]ThreadPool.QueueUserWorkItem的性能问题


 

引用:http://blog.csdn.net/sq_zhuyi/article/details/6869661

在WEB开发中,为了减少页面等待时间提高用户体验,我们往往会把一些浪费时间的操作放到新线程中在后台运行。

简单的实现代码就是:

//代码一new Thread(()=>{  //do something}).Start();

View Code

但是对于一个请求量大的网址这样做是很不现实的——每一个操作都要开启一个新线程,最终会因CPU不堪重负而使网站挂掉。

更好的做法是使用线程队列。

对于线程队列 ThreadPool.QueueUserWorkItem 很多人应该都不陌生,下边看微软的解释:

将方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。

它的作用就是将一些操作放入当前线程之外的另外一个线程中执行,它的使用方法很简单:

 

//代码二ThreadPool.QueueUserWorkItem(stat => {  //do something}, null);

View Code

它相对代码一的优点是会利用已经创建过的空闲的线程,如果没有空闲就排队,而不会盲目的一直创建下去。

但是它并没有摆脱“创建新线程”的问题:过多的线程会占用更多的资源。由此我们不难想到,我们为什么不自己搞个队列,让它们在同一个线程中逐个执行?对此,我写了个简单的实现类:

 1     public class BackgroundTasks 2     { 3       private class TaskEntity 4       { 5         public TaskEntity(Action<object> func, object data) 6         { 7           this.Function = func; 8           this.Data = data; 9         }10         public Action<object> Function;11         public object Data;12       }13       static Queue<TaskEntity> list = new Queue<TaskEntity>();14 15       static BackgroundTasks()16       {17         Thread th = new Thread(RunTask);18         th.IsBackground = true;19         th.Start();20       }21       static void RunTask()22       {23         while (true)24         {25           if (list.Count == 0)26           {27             Thread.Sleep(1000);28           }29           else30           {31             TaskEntity entity;32             lock (list)33             {34               entity = list.Dequeue();35             }36             try37             {38               entity.Function(entity.Data);39             }40             catch { }41             Thread.Sleep(10);42           }43         }44       }45 46       public static void Add(Action<object> func, object data)47       {48         lock (list)49         {50           list.Enqueue(new TaskEntity(func, data));51         }52       }53 54     }

View Code

该类的使用很简单:

BackgroundTasks.Add((obj)=>{  Console.WriteLine("这个任务的添加时间是:{0}", obj as DateTime);}, DateTime.Now);

还有一个“实例版”的,就是针对每个方法,分别创建一个任务队列:

 1 public class BackgroundTasks01<T> 2 { 3   private Action<T> Function; 4  5   private Queue<T> list = new Queue<T>(); 6  7   public BackgroundTasks01(Action<T> func) 8   { 9     this.Function = func;10 11     Thread th = new Thread(RunTask);12     th.IsBackground = true;13     th.Start();14   }15   private void RunTask()16   {17     while (true)18     {19       if (list.Count == 0)20       {21         Thread.Sleep(1000);22       }23       else24       {25         T data;26         lock (list)27         {28           data = list.Dequeue();29         }30         try31         {32           Function(data);33         }34         catch { }35         Thread.Sleep(10);36       }37     }38   }39 40   public void Add(T data)41   {42     lock (list)43     {44       list.Enqueue(data);45     }46   }47 48 }

View Code

blog类:

class Blog{  private int _blogId = 0;  public int BlogId  {    get { return _blogId; }    set { _blogId = value; }  }  private string _blogName = "";  public string BlogName  {    get { return _blogName; }    set { _blogName = value; }  }}

View Code

调用示例:

//方法二:var bg = new BackgroundTasks01<Blog>((blog) =>{  Console.WriteLine("BlogName:{0},BlogId:{1}", blog.BlogName, blog.BlogId);});int i = 0;while (i++ < 100){  bg.Add(new Blog() { BlogId = i, BlogName = "Default" });}Console.ReadLine();bg.Add(new Blog() { BlogId = 1000, BlogName = "张三" });bg.Add(new Blog() { BlogId = 1001, BlogName = "李四" });

View Code

这个设计既解决了异步执行,又解决了占用资源的问题。

但是世界上没有完美的东西,代码也是如此,由于队列中的任务是单线程执行,可能会导致某些任务在很长时间后才会被执行到,或者重启IIS导致很多任务还没有被执行就被丢弃。

无论怎么,这种设计还是适用于很多“一般情况”。