欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

多线程之旅(ThreadPool 线程池)

程序员文章站 2022-11-17 12:59:00
一、什么是ThreadPool 线程池(源码) 1.线程池顾名思义,有我们的系统创建一个容器装载着我们的线程,由CLR控制的所有AppDomain共享。线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。所以使用线程池不需要自己创建线程,而是通过线程池来创建和执行和管 ......

一、什么是threadpool 线程池(源码

      1.线程池顾名思义,有我们的系统创建一个容器装载着我们的线程,由clr控制的所有appdomain共享。线程池可用于执行任务、发送工作项、处理异步 i/o、代表其他线程等待以及处理计时器。所以使用线程池不需要自己创建线程,而是通过线程池来创建和执行和管理线程。

二、threadpool 线程池和线程的区别

      1.threadpool 线程池是在.net 2.0出现的,是一个享元模式整个程序共同享用这一个线程池,当我们的线程执行任务之后它不会立刻销毁,它会回到线程池中,如果有新的任务它就会去执行。避免了我们线程的重复创建和销毁(也不会造成我们cpu的上下文切换的损耗)。

      2.大家仔细看一下我前面写的thread 创建线程执行任务之后,它会自动销毁。那问题来了我们经常的创建、销毁线程这可都是资源的浪费呀!!所以我们要利用每个线程占有的资源。

三、threadpool 线程池缺点

       1.线程池在性能上优于线程,但是它也是有缺点的。它不支持线程的取消、完成、失败通知等交互性操作。

       2.它不能设置池中线程的name,会增加使用者的难度。

  3.线程池中线程通常都是后台线程,优先级为threadpriority.normal

  4.线程池堵塞会影响我们的性能,阻塞会使clr错误地认为它占用了大量cpu。clr能够检测或补偿(往池中注入更多线程),但是这可能使线程池受到后续超负荷的印象。task (也及时后面要讲的)解决了这个问题。

  5.线程池使用的是全局队列,全局队列中的线程依旧会存在竞争共享资源的情况,从而影响性能(task 解决了这个问题,方案是使用本地队列)。

四、线程池工作原理

  1.clr初始化时,线程池中是没有线程的,但是内部有一个操作请求队列,当我们的应用程序使用异步时,会将一个记录项添加到线程池的队列中,线程池队列会自动读取这个记录项,并且发给一个线程池的线程,如果线程池没有线程就会创建一个线程执行这任务,当线程完成任务它不会自动销毁而是回到我们的线程池中,等待线程池派发新的任务。

  2.如果程序给线程池派发了很多任务,线程池也会使用这一个线程执行所有的任务,如果我们的请求速度大于了我们的线程处理速度,就会创建额外线程,就不会导致我们创建了过多的线程。

  2.当我们的线程有大量休息的,它们会在一段时间内自动销毁。这样很好的控制了我们应用程序的性能。

五、threadpool 线程池使用

  1.threadpool是一个静态类,调用queueuserworkitem方法,是可以将一个异步计算放入我们的线程池队列中。

public static bool queueuserworkitem(waitcallback callback);
public static bool queueuserworkitem(waitcallback callback, object state);
方法 说明
queueuserworkitem 启动线程池里的一个线程(工作者线程)
getminthreads 检索线程池在新请求预测中能够按需创建的线程的最小数量。
getmaxthreads 最多可用线程数,所有大于此数目的请求将保持排队状态,直到线程池线程由空闲。
getavailablethreads 剩余空闲线程数。
setmaxthreads 设置线程池中的最大线程数(请求数超过此值则进入队列)。
setminthreads 设置线程池最少需要保留的线程数。

 

 

 

 

 

 

  2.我们可以看到threadpool比thread少了很多的api,被砍掉了

/// <summary>
        /// threadpool的使用
        /// workerthreads  clr线程池分为工作者线程(workerthreads)
        /// completionportthreads i/o线程(completionportthreads)
        /// </summary>
        public static void show()
        {
            //使用线程
            threadpool.queueuserworkitem((x) => running());
            threadpool.getavailablethreads(out int workerthreads, out int completionportthreads);
            console.writeline($"没有设置线程数之前 workerthreads:{workerthreads}  completionportthreads:{completionportthreads}");
            //设置最大的线程数
            threadpool.setmaxthreads(16, 16);
            //设置最小的线程数
            threadpool.setminthreads(8, 8);
            threadpool.getavailablethreads(out int workerthreads1, out int completionportthreads1);
            console.writeline($"设置最大的线程数之后 workerthreads:{workerthreads1}  completionportthreads:{completionportthreads1}");
            console.readline();
        }

多线程之旅(ThreadPool 线程池)

   3.我们要注意的就是堵塞线程的时候一定要做好处理,最好是不要堵塞我们的线程,不然很容易造成死锁gg

        /// <summary>
        /// threadpool 线程等待
        ///类  包含了一个bool属性
        ///false--waitone等待--set--true--waitone直接过去
        ///true--waitone直接过去--reset--false--waitone等待
        ///https://www.cnblogs.com/howtrace/p/11362284.html
        /// </summary>
        public static void show1()
        {
            //设置最大的线程数
            threadpool.setmaxthreads(16, 16);
            //设置最小的线程数
            threadpool.setminthreads(8, 8);
            //设置false使用waitone()会直接堵塞线程,不会释放 、set()设置为true
            manualresetevent manualresetevent = new manualresetevent(false);
            //设置false使用waitone()会直接堵塞线程,不会释放 、set()设置为true
            autoresetevent autoresetevent = new autoresetevent(false);
            //上面两种方法都是可以拦截线程,都是继承eventwaithandle 接口
            //就都具有reset() //红灯 设置为false导致线程等待
            //set() //绿灯 设置为true 启动线程继续执行
            //waitone() // 等待信号 会根据我们线程状态执行,为true不需要等待直接执行
            //反之为false会等待线程状态为true才会执行

            //不同点 manualresetevent autoresetevent
            //manualresetevent 在·使用set()的时候会所有处理 waitone 状态线程均继续执行。
            //autoresetevent 在使用set()的时候会执行一个线程其他的线程继续等待执行。

            for (int i = 0; i < 20; i++)
            {
                var k = i;
                threadpool.queueuserworkitem(x =>
                {
                    console.writeline(k);
                    if (k < 18)
                    {
                        //等待线程,但是上面我们只开了16个线程,结果我18个线程全部等待
                        //导致了死锁
                        manualresetevent.waitone();
                    }
                    else
                    {
                        //恢复执行状态
                        manualresetevent.set();
                    }
                });
                if (manualresetevent.waitone())
                {
                    console.writeline("没有死锁、、、");
                }
                console.writeline("等着queueuserworkitem完成后才执行");
            }
            console.readline();
        }