ThreadPool.QueueUserWorkItem的性能问题

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

简单的实现代码就是:

[csharp]  view plain  copy
 print ?
  1. //代码一  
  2. new Thread(()=>{  
  3. //do something  
  4. }).Start();  

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

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


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

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


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

[csharp]  view plain  copy
 print ?
  1. //代码二  
  2. ThreadPool.QueueUserWorkItem(stat => {  
  3. //do something  
  4. }, null);  

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


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

[csharp]  view plain  copy
 print ?
  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.             else  
  30.             {  
  31.                 TaskEntity entity;  
  32.                 lock (list)  
  33.                 {  
  34.                     entity = list.Dequeue();  
  35.                 }  
  36.                 try  
  37.                 {  
  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. }  

该类的使用很简单:

BackgroundTasks.Add((obj)=>{

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

}, DateTime.Now);

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

[csharp]  view plain  copy
 print ?
  1. public class BackgroundTasks<T>  
  2. {  
  3.     private Action<T> Function;  
  4.   
  5.     private Queue<T> list = new Queue<T>();  
  6.   
  7.     public BackgroundTasks(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.             else  
  24.             {  
  25.                 T data;  
  26.                 lock (list)  
  27.                 {  
  28.                     data = list.Dequeue();  
  29.                 }  
  30.                 try  
  31.                 {  
  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. }  

调用示例:

[csharp]  view plain  copy
 print ?
  1. var bg = new BackgroundTasks<Blog>((blog) => {   
  2.     Console.WriteLine(blog.BlogId);   
  3. });  
  4. int i = 0;  
  5. while (i++ < 1000)  
  6. {  
  7.     bg.Add(new Blog() { BlogId = i });  
  8. }  


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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值