[转]ThreadPool.QueueUserWorkItem的性能问题

本文介绍了一种改进的后台任务执行方式,通过引入线程队列来优化页面响应速度,同时避免了大量线程导致的资源消耗问题。详细阐述了如何在单一线程中逐个执行任务,以及针对特定需求的自定义任务队列实现,适用于大多数常见场景。

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

简单的实现代码就是:

[csharp] view plain copy print ?
  1. //代码一 
  2. new Thread(()=>{ 
  3. //do something 
  4. }).Start(); 
//代码一 new Thread(()=>{ //do something }).Start();
但是对于一个请求量大的网址这样做是很不现实的——每一个操作都要开启一个新线程,最终会因CPU不堪重负而使网站挂掉。

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

 

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

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

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

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

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

 

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

[csharp] view plain copy print ?
  1. public class BackgroundTasks 
  2.     private class TaskEntity 
  3.     { 
  4.         public TaskEntity(Action<object> func, object data) 
  5.         { 
  6.             this.Function = func; 
  7.             this.Data = data; 
  8.         } 
  9.         public Action<object> Function; 
  10.         public object Data; 
  11.     } 
  12.     static Queue<TaskEntity> list = new Queue<TaskEntity>(); 
  13.      
  14.     static BackgroundTasks() 
  15.     { 
  16.         Thread th = new Thread(RunTask); 
  17.         th.IsBackground = true
  18.         th.Start(); 
  19.     } 
  20.     static void RunTask() 
  21.     { 
  22.         while (true
  23.         { 
  24.             if (list.Count==0) 
  25.             { 
  26.                 Thread.Sleep(1000); 
  27.             } 
  28.             else 
  29.             { 
  30.                 TaskEntity entity; 
  31.                 lock (list) 
  32.                 { 
  33.                     entity = list.Dequeue(); 
  34.                 } 
  35.                 try 
  36.                 { 
  37.                     entity.Function(entity.Data); 
  38.                 } 
  39.                 catch { } 
  40.                 Thread.Sleep(10); 
  41.             } 
  42.         } 
  43.     } 
  44.  
  45.     public static void Add(Action<object> func, object data) 
  46.     { 
  47.         lock (list) 
  48.         { 
  49.             list.Enqueue(new TaskEntity(func, data)); 
  50.         } 
  51.     } 
  52.  
public class BackgroundTasks { private class TaskEntity { public TaskEntity(Action<object> func, object data) { this.Function = func; this.Data = data; } public Action<object> Function; public object Data; } static Queue<TaskEntity> list = new Queue<TaskEntity>(); static BackgroundTasks() { Thread th = new Thread(RunTask); th.IsBackground = true; th.Start(); } static void RunTask() { while (true) { if (list.Count==0) { Thread.Sleep(1000); } else { TaskEntity entity; lock (list) { entity = list.Dequeue(); } try { entity.Function(entity.Data); } catch { } Thread.Sleep(10); } } } public static void Add(Action<object> func, object data) { lock (list) { list.Enqueue(new TaskEntity(func, data)); } } }
该类的使用很简单:

 

BackgroundTasks.Add((obj)=>{

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

}, DateTime.Now);

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

 

[csharp] view plain copy print ?
  1. public class BackgroundTasks<T> 
  2.     private Action<T> Function; 
  3.  
  4.     private Queue<T> list = new Queue<T>(); 
  5.  
  6.     public BackgroundTasks(Action<T> func) 
  7.     { 
  8.         this.Function = func; 
  9.  
  10.         Thread th = new Thread(RunTask); 
  11.         th.IsBackground = true
  12.         th.Start(); 
  13.     } 
  14.     private void RunTask() 
  15.     { 
  16.         while (true
  17.         { 
  18.             if (list.Count == 0) 
  19.             { 
  20.                 Thread.Sleep(1000); 
  21.             } 
  22.             else 
  23.             { 
  24.                 T data; 
  25.                 lock (list) 
  26.                 { 
  27.                     data = list.Dequeue(); 
  28.                 } 
  29.                 try 
  30.                 { 
  31.                     Function(data); 
  32.                 } 
  33.                 catch { } 
  34.                 Thread.Sleep(10); 
  35.             } 
  36.         } 
  37.     } 
  38.  
  39.     public void Add(T data) 
  40.     { 
  41.         lock (list) 
  42.         { 
  43.             list.Enqueue(data); 
  44.         } 
  45.     } 
  46.  
public class BackgroundTasks<T> { private Action<T> Function; private Queue<T> list = new Queue<T>(); public BackgroundTasks(Action<T> func) { this.Function = func; Thread th = new Thread(RunTask); th.IsBackground = true; th.Start(); } private void RunTask() { while (true) { if (list.Count == 0) { Thread.Sleep(1000); } else { T data; lock (list) { data = list.Dequeue(); } try { Function(data); } catch { } Thread.Sleep(10); } } } public void Add(T data) { lock (list) { list.Enqueue(data); } } }
调用示例:

 

 

[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.     bg.Add(new Blog() { BlogId = i }); 
var bg = new BackgroundTasks<Blog>((blog) => { Console.WriteLine(blog.BlogId); }); int i = 0; while (i++ < 1000) { bg.Add(new Blog() { BlogId = i }); }

 

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

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

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

转载于:https://www.cnblogs.com/luqingfei/archive/2012/04/25/2470717.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值