Linux之线程池

线程池

一、线程池基本概念

线程池是什么?
顾名思义,就是把一堆开辟好的线程放在一个池子里统一管理,就是一个线程池。

  • 线程池是预先创建线程的一种技术。线程池在任务还没有到来之前,创建一定数量(N)的线程,放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。
  • 当新任务到来时,缓冲池选择一个空闲线程,把任务传入此线程中运行;如果缓冲池已经没有空闲线程,则新建若干个线程。当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。

为什么要用线程池,难道来一个请求给它申请一个线程,请求处理完了释放线程不行么?也行,但是如果创建线程和销毁线程的时间比线程处理请求的时间长,而且请求很多的情况下,我们的CPU资源都浪费在了创建和销毁线程上了,所以这种方法的效率比较低,于是,我们可以将若干已经创建完成的线程放在一起统一管理,如果来了一个请求,我们从线程池中取出一个线程来处理,处理完了放回池内等待下一个任务,线程池的好处是避免了繁琐的创建和结束线程的时间,有效的利用了CPU资源

二、线程池工作原理

2.1 线程池类至少提供三个接口,初始化线程池、销毁线程池、添加任务接口

  • 初始化线程池

    • 开启线程池调度器线程
    • 预先创建N个线程(由线程调度池器类负责创建线工作者线程),放入空闲线程队列
    • 指定最大的忙碌状态的线程数
  • 销毁线程池

    • 释放空闲队列中的线程与工作状态中的线程
    • 释放调度器线程
  • 添加任务
    添加一实际任务,但是并没有立刻运行该任务,只是放入任务队列,由线程池调度器从任务队列获取该任务,并从线程池中获得一个线程来运行该任务,这里实际上是一种生产者消费者模型。

2.2线程池调度器包含创建空闲线程、销毁空闲线程接口

2.3线程池调度器本身也是一个线程,主要负责任务调度与线程调度,其工作过程大致如下:

  • 从任务队列获取任务,如果队列为空,阻塞等待新任务到来;
  • 队列不为空,取出该任务,从空闲线程队列取一线程,如果为空,判断工作者线程数是否达到上限,如果没有,则创建若个空闲线程,否则等待某一任务执行完毕,并且该任务对应的线程归还给线程池;
  • 获得空闲工作者线程,将任务交给工作者线程来处理,工作者线程维护一任务指针,这里只要该指针指向任务,并且唤醒线程;
  • 判断空闲工作者线程数是否超过最大工作者线程数,如果超过,销毁(空闲线程数-允许最大空闲线程数)个线程。

三、如何才能创建一个线程池的模型

  • 线程池结构,它负责管理多个线程并提供任务队列的接口;
  • 工作线程,它们负责处理任务;
  • 任务队列,存放待处理的任务。

四、线程池所需要的数据结构

4.1 0/1信号量

用于当任务队列非空时通知线程,这里是用互斥锁和条件变量来实现的信号量,其实POSIX信号量的一种实现就是用的互斥锁和条件变量

/* Binary semaphore */
typedef struct bsem {
    pthread_mutex_t mutex;
    pthread_cond_t   cond;
    int v;   //v的值非0即1
} bsem;

4.2 标识任务的结构体

/* Job */
typedef struct job{
    struct job*  prev;                   /* pointer to previous job   */
    void*  (*function)(void* arg);       /* function pointer          */
    void*  arg;                          /* function's argument       */
} job;

4.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.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,对于每个线程来说,都提供对所在线程池的访问

4.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提供对线程池数据的互斥访问,同时,线程池需要和任务队列协作,所以还要提供对任务队列的访问。

参考:https://www.cnblogs.com/sjinsa/p/4767716.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值