• 操作系统闲谈03——线程池


    线程池

    一、基本原理

    线程池采用了预创建技术,提前创建好一定数量的线程,有新的任务到来时直接处理对应的任务。

    线程池主要包括了三部分:

    任务队列、工作线程、管理者线程

    • 任务队列,存储需要处理的任务,由工作的线程来处理这些任务

    (1)通过线程池提供的API函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除;

    (2)已处理的任务将被删除;

    (3)线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程。

    • 工作线程(任务队列中任务的消费者),N个

    (1)线程池维护了一定数量的工作线程,它们的作用是不断的读取任务队列,从里边取出任务并处理;

    (2)如果任务队列为空,工作线程将会被阻塞(使用条件变量或信号量阻塞);

    (3)如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作;

    • 管理者线程(不处理任务队列中的任务),1个

    (1)它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测;

    (2)当任务过多的时候,可以适当的创建一些新的工作线程;

    (3)当任务过少的时候,可以适当的销毁一些工作线程;

    大致过程如下:

    image-20221027095641247

    二、为什么要有线程池

    (1)降低资源消耗。通过重复利用已创建好的线程来降低线程创建和销毁时给系统带来的消耗。
    (2)提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即得到处理。
    (3)提高线程的可管理性。我们可以对线程池里的线程进行统一的分配,调优和监控。

    问题:创建的线程越多性能越高,对吗?

    答:不对。线程数量越多,可能会导致线程切换越频繁, 进而还有可能导致程序运行效率降低。多线程程序的运行效率, 呈现为正态分布, 线程数量从最开始的1开始逐渐增加, 程序的运行效率也逐渐变高, 直到线程数量达到一个临界值, 然后再次增加线程数量时, 程序的运行效率会减小(主要是由于频繁地线程切换影响到了整体线程运行效率)。

    三、模拟实现线程池

    01 成员变量

    线程池的成员变量:

    • 任务队列,quueu q;
    • 线程个数,int num;
    • 互斥量,pthread_mutex_t lock;
    • 条件变量,pthread_cond_t cond;

    image-20221102103710586

    02 代码实现

    • Makefile文件
    threadpool:threadpool.cc    
      g++ $^ -o $@ -lpthread                                                                                                                                     
        
    clean:    
      rm -f threadpool
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • Task.hpp
    #pragma  once    
    #include    
    #include    
    using namespace std;    
    class Task    
    {    
      private:    
        int x;int y;    
        char op;                                                                                                                                                 
      public:       
        Task(int _x,int _y,char _op)    
          :x(_x)                        
          ,y(_y)    
          ,op(_op){ }    
        Task()           
        {};       
        void Run()    
        {             
          int ret=0;    
          switch(op){    
            case '+':    
              ret=x+y;    
              break;                     
            case '-':                    
              ret=x-y;                   
              break;                     
            case '*':            
              ret=x*y;
              break;
              case '/':
              ret=x/y;
              break;
              case '%':
              ret=x%y;
              break;
          }
          printf("%d %c %d = %d\n",x,op,y,ret);
        } 
    };                                    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • threadpool.hpp
    #pragma once    
    #include                                                                                                                                           
    #include    
    #include    
    #include    
    using namespace std;    
        
        
    #define NUM 4    
    template    
    class ThreadPool    
    {    
      private:    
        queue q;//任务队列    
        int thread_num;//线程池的线程数量    
        pthread_mutex_t lock;//互斥锁    
        pthread_cond_t cond;//条件变量    
      public:    
        ThreadPool(int num=NUM)//初始化变量
          :thread_num(num){    
            pthread_mutex_init(&lock,NULL);    
            pthread_cond_init(&cond,NULL);    
        }    
        
        bool Empty()    
        {    
          return q.size()==0?true:false;     
        }    
        
       static  void* Routine(void* arg)//线程执行流    
        {    
          pthread_detach(pthread_self());//线程分离    
          ThreadPool* self=(ThreadPool*)arg;
          while(1)
          {
            self->LockQueue();
            while(self->Empty())//任务队列是否为空
            {
              self->Wait();
            }
            T data;
            self->Pop(data);//取出任务
            self->UnlockQueue();
     
            cout<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • threadpool.cc文件
    #include"threadpool.hpp"    
    #include"Task.hpp"    
    #include    
    #include    
    int main()    
    {    
      ThreadPool<Task> * q=new ThreadPool<Task>();//创建线程池    
      q->ThreadPoolInit();    
      srand((long int)time(NULL));    
      while(1)  //主线程往任务队列中放任务  
      {    
        char arr[]="+-*/%";    
        int x=rand()%100+1;    
        int y=rand()%100+1;    
        char op=arr[rand()%5];    
        Task t(x,y,op);//创建任务    
        q->Push(t);//将任务推送给队列中    
      }                                                                                                                                                          
      return 0;    
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:

    image-20221102103956180

  • 相关阅读:
    低代码:数字化转型趋势下的快速开发方式
    RabbitMQ - Spring boot 整合 RabbitMQ
    Cholesterol-PEG-Amine,CLS-PEG-NH2,胆固醇-聚乙二醇-氨基科研用试剂
    MATLAB中alignsignals函数使用
    MySQL 开启配置binlog以及通过binlog恢复数据
    【大数据ETL工具,Kettle的学习和使用】
    用于持续医疗监测的无袖带血压估计算法【翻译】
    基于大仓库的微服务差异化构建工具
    Java——Spring的控制反转(一文详解IOC)
    HuggingFace 国内下载 阿里云盘下载速度20MB/s
  • 原文地址:https://blog.csdn.net/qq_41945053/article/details/127647581