• ThreadPool.QueueUserWorkItem的性能问题


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

    简单的实现代码就是:

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

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

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

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

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

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

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

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

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

    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);

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

    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);  
            }  
        }    }  
    

      调用示例:

    var bg = new BackgroundTasks<Blog>((blog) => {   
        Console.WriteLine(blog.BlogId);   
    });  
    int i = 0;  
    while (i++ < 1000)  
    {  
        bg.Add(new Blog() { BlogId = i });  
    }
    

      

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

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

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

      

  • 相关阅读:
    CDN内容交付网络学习
    mysql高可用架构了解
    coredump了解
    NTP协议了解
    mysql主从复制学习
    Go中json的marshal解析
    go基础系列~基础环境相关
    leetcode(c++)(背包问题)
    arcsin是反正弦。
    设计模式的六大原则。简称:SOLID
  • 原文地址:https://www.cnblogs.com/glj161/p/3072796.html
Copyright © 2020-2023  润新知