ThreadPool.QueueUserWorkItem的性能问题

来源:互联网 发布:淘宝短链接生成 编辑:程序博客网 时间:2024/05/16 08:15

在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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 瑜伽垫容易出现痕迹怎么办 37岁失业了该怎么办 45岁找什么工作怎么办 华为手机4g网速慢怎么办 解析软件包时出现问题怎么办 一字马不能下去髋摆不正怎么办 练轮瑜伽骆驼式腰疼怎么办 感昌咳嗽老不好怎么办 我感昌一直不好怎么办 腰间盘突出晚上睡觉痛怎么办 天天吃撑了怎么办啊 一吸气就想咳嗽怎么办 鼻子堵住了怎么办没法吸气时 一只鼻子不通气怎么办 练瑜伽后特别饿怎么办 站一天小腿肿了怎么办 练腹肌腰粗了怎么办 大专不交学费.然后退学怎么办 练瑜伽压腿一字马受伤了怎么办 银行工作人员借钱不还怎么办 借钱不还跑了但有工作怎么办 亲戚家借钱不还怎么办 学习瑜伽教练口令好复杂怎么办 练瑜伽腿的柔韧性不够怎么办 瑜伽扭转时手抓不到脚怎么办 练瑜伽腿部太硬怎么办 褶皱衣服不紧了怎么办 吃撑了肚子涨怎么办 正好压本科线该怎么办 大脚趾被砸了怎么办 脊柱侧弯每天疼怎么办 内衣扣的位置脊柱疼怎么办 练完瑜伽颈椎疼怎么办 乳胶枕头太高了怎么办 枕头太高脖子痛怎么办 颈椎突出症状缓解后怎么办 外痔疼的的历害怎么办 小肚子疼得历害怎么办 练瑜伽伤到颈椎怎么办 鼻子干口干胃烧怎么办 颈椎病压迫神经引起手麻怎么办