加入收藏 | 设为首页 | 会员中心 | 我要投稿 拼字网 - 核心网 (https://www.hexinwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux下线程池的理解与简单实现

发布时间:2022-12-01 13:09:24 所属栏目:Linux 来源:
导读:  首先,线程池是什么?顾名思义,就是把一堆开辟好的线程放在一个池子里统一管理,就是一个线程池。

  其次,为什么要用线程池,难道来一个请求给它申请一个线程,请求处理完了释放线程不行么?也行,但是如
  首先,线程池是什么?顾名思义,就是把一堆开辟好的线程放在一个池子里统一管理,就是一个线程池。
 
  其次,为什么要用线程池,难道来一个请求给它申请一个线程,请求处理完了释放线程不行么?也行,但是如果创建线程和销毁线程的时间比线程处理请求的时间长,而且请求很多的情况下,我们的CPU资源都浪费在了创建和销毁线程上了,所以这种方法的效率比较低,于是,我们可以将若干已经创建完成的线程放在一起统一管理线程池linux,如果来了一个请求,我们从线程池中取出一个线程来处理,处理完了放回池内等待下一个任务,线程池的好处是避免了繁琐的创建和结束线程的时间,有效的利用了CPU资源。
 
  按照我的理解,线程池的作用和双缓冲的作用类似,可以完成任务处理的“鱼贯”动作。
 
  最后,如何才能创建一个线程池的模型呢,一般需要以下三个参与者:
 
  1、线程池结构,它负责管理多个线程并提供任务队列的接口
 
  2、工作线程,它们负责处理任务
 
  3、任务队列,存放待处理的任务
 
  有了三个参与者,下一个问题就是怎么使线程池安全有序的工作,可以使用POSIX中的信号量、互斥锁和条件变量等同步手段。有了这些认识,我们就可以创建自己的线程池模型,我在github上找了一个比较经典的线程池的例子,有兴趣的可以学习一下。
 
  原作者github地址:
 
  线程池所需要的数据结构:
 
  (1)、0/1信号量,用于当任务队列非空时通知线程,这里是用互斥锁和条件变量来实现的信号量,其实POSIX信号量的一种实现就是用的互斥锁和条件变量
 
  /* Binary semaphore */
  typedef struct bsem {
      pthread_mutex_t mutex;
      pthread_cond_t   cond;
      int v;   //v的值非0即1
  } bsem;
  (2)、标识任务的结构体,prev指向的对象是当前任务的前一个任务,这里用pnext来标识更贴切
 
  /* Job */
  typedef struct job{
      struct job*  prev;                   /* pointer to previous job   */
      void*  (*function)(void* arg);       /* function pointer          */
      void*  arg;                          /* function's argument       */
  } job;
  (3)、工作队列
 
  /* Job queue */
  typedef struct jobqueue{
      pthread_mutex_t rwmutex;             /* used for queue r/w access */
      job  *front;                         /* pointer to front of queue */
      job  *rear;                          /* pointer to rear  of queue */
      bsem *has_jobs;                      /* flag as binary semaphore  */
      int   len;                           /* number of jobs in queue   */
  } jobqueue;
  互斥锁rwmutex用来同步对工作队列的读写操作,front用来标识工作队列中的第一个任务,rear用来标识工作队列中的最后一个任务,has_jobs用来提供对二值信号量的访问接口,len代表当前工作队列中的任务数量。
 
  (4)、工作线程
 
  /* Thread */
  typedef struct thread{
      int       id;                        /* friendly id               */
      pthread_t pthread;                   /* pointer to actual thread  */
      struct thpool_* thpool_p;            /* access to thpool          */
  } thread;
  id标识第几个线程,pthread代表的是创建的真正的线程id,对于每个线程来说,都提供对所在线程池的访问
 
  (5)、线程池结构
 
  /* Threadpool */
  typedef struct thpool_{
      thread**   threads;                  /* pointer to threads        */
      volatile int num_threads_alive;      /* threads currently alive   */
      volatile int num_threads_working;    /* threads currently working */
      pthread_mutex_t  thcount_lock;       /* used for thread count etc */
      jobqueue*  jobqueue_p;               /* pointer to the job queue  */    
  } thpool_;
  threads可以看做是一个指针数组,数组中的每个指针都指向一个线程结构,num_threads_alive标识的是线程池中有多少个可工作线程,num_threads_working代表的是当前线程池中正在工作的线程数目,互斥锁thcount_lock提供对线程池数据的互斥访问,同时,线程池需要和任务队列协作,所以还要提供对任务队列的访问。
 
  线程池的工作流程:
 
  初始化线程池、任务队列和工作线程->向任务队列中添加任务->将等候在条件变量(任务队列上有任务)上的一个线程唤醒并从该任务队列中取出第一个任务给该线程执行->等待任务队列中所有任务执行完毕->关闭线程池。
 

(编辑:拼字网 - 核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!