多线程之旅(ThreadPool 线程池)

1、什么是ThreadPool 线程池(源码html

      1.线程池顾名思义,有咱们的系统建立一个容器装载着咱们的线程,由CLR控制的全部AppDomain共享。线程池可用于执行任务、发送工做项、处理异步 I/O、表明其余线程等待以及处理计时器。因此使用线程池不须要本身建立线程,而是经过线程池来建立和执行和管理线程。git

2、ThreadPool 线程池和线程的区别多线程

      1.ThreadPool 线程池是在.NET 2.0出现的,是一个享元模式整个程序共同享用这一个线程池,当咱们的线程执行任务以后它不会马上销毁,它会回到线程池中,若是有新的任务它就会去执行。避免了咱们线程的重复建立和销毁(也不会形成咱们CPU的上下文切换的损耗)。异步

      2.你们仔细看一下我前面写的Thread 建立线程执行任务以后,它会自动销毁。那问题来了咱们常常的建立、销毁线程这可都是资源的浪费呀!!因此咱们要利用每一个线程占有的资源。ide

3、ThreadPool 线程池缺点性能

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

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

  3.线程池中线程一般都是后台线程,优先级为ThreadPriority.Normalcode

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

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

4、线程池工做原理

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

  2.若是程序给线程池派发了不少任务,线程池也会使用这一个线程执行全部的任务,若是咱们的请求速度大于了咱们的线程处理速度,就会建立额外线程,就不会致使咱们建立了过多的线程。

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

5、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();
        }
View Code

   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();
        }
View Code
相关文章
相关标签/搜索