• ThreadPool.QueueUserWorkItem的性能问题


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

    简单的实现代码就是:

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

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

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


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

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


    它的作用就是将一些操作放入当前线程之外的另外一个线程中运行,它的用法非常easy:

    //代码二
    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));
                }
            }
    
        }

    该类的使用非常easy:

    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导致非常多任务还没有被运行就被丢弃。

    不管怎么,这样的设计还是适用于非常多“普通情况”。


  • 相关阅读:
    今週のschedule
    软件架构师应该知道的97件事
    没办法的复习
    优秀程序员的45个习惯
    程序员如何追女孩
    那些相见恨晚的 JavaScript 技巧
    CodeSmith开发系列资料总结
    HR的至高机密:20个公司绝对不会告诉你的潜规则
    asp.net页面出错时的处理方法
    Asp.net 文件上传的 FileUpload FileName 和 FileUpload PostedFile.FileName的细节问题
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/3963891.html
Copyright © 2020-2023  润新知