zoukankan      html  css  js  c++  java
  • Linux下线程池的理解与简单实现

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

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

      按照我的理解,线程池的作用和双缓冲的作用类似,可以完成任务处理的“鱼贯”动作。

      最后,如何才能创建一个线程池的模型呢,一般需要以下三个参与者:

        1、线程池结构,它负责管理多个线程并提供任务队列的接口

        2、工作线程,它们负责处理任务

        3、任务队列,存放待处理的任务

      有了三个参与者,下一个问题就是怎么使线程池安全有序的工作,可以使用POSIX中的信号量、互斥锁和条件变量等同步手段。有了这些认识,我们就可以创建自己的线程池模型,我在github上找了一个比较经典的线程池的例子,有兴趣的可以学习一下。

      原作者github地址:https://github.com/Pithikos/C-Thread-Pool

       线程池所需要的数据结构:

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

      线程池的工作流程:

        初始化线程池、任务队列和工作线程->向任务队列中添加任务->将等候在条件变量(任务队列上有任务)上的一个线程唤醒并从该任务队列中取出第一个任务给该线程执行->等待任务队列中所有任务执行完毕->关闭线程池。

        

  • 相关阅读:
    FZU 2150 Fire Game
    POJ 3414 Pots
    POJ 3087 Shuffle'm Up
    POJ 3126 Prime Path
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    字符数组
    HDU 1238 Substing
    欧几里德和扩展欧几里德详解 以及例题CodeForces 7C
    Codeforces 591B Rebranding
  • 原文地址:https://www.cnblogs.com/sjinsa/p/4767716.html
Copyright © 2011-2022 走看看