zoukankan      html  css  js  c++  java
  • [转]ThreadPool.QueueUserWorkItem的性能问题

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

    简单的实现代码就是:

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

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

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

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

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

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

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

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

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

    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.  

    该类的使用很简单:

    BackgroundTasks.Add((obj)=>{

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

    }, DateTime.Now);

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

    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.  

    调用示例:
    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 }); 


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

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

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

  • 相关阅读:
    importlib
    js给kindeditor添加值
    在kindeditor 获取textarea 中 输入的值
    获取lable选中时触发事件
    Django之ModelForm组件
    KindEditor 和 xss过滤
    from 动态显示select数据
    CBV 验证装饰器的使用
    views获取数据 -- request包含的方法
    django -- 自定义simpletag 和 filter
  • 原文地址:https://www.cnblogs.com/luqingfei/p/2470717.html
Copyright © 2011-2022 走看看