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                     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         }
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             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 }
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导致很多任务还没有被执行就被丢弃。

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

posted @ 2015-06-28 10:58  升级者  阅读(678)  评论(0编辑  收藏  举报