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

  线程池的工作流程:

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

    

时间: 2024-10-26 10:25:40

Linux下线程池的理解与简单实现的相关文章

线程池的理解与简单实现

池 由于服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是以空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.这就是池的概念. 池是一组资源的集合,这组资源在服务器启动之初就被创建并初始化,这称为静态资源分配. 当服务器进入正式运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配.很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的. 当服务器处理完一个客户

谈一谈linux下线程池

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

Linux 下线程的理解

2017-04-03 最近深入研究了下Linux线程的问题,发现自己之前一直有些许误解,特记之-- 关于Linux下的线程,各种介绍Linux的书籍都没有深入去解释的,或许真的如书上所述,Linux本质上不存在线程的概念!在某种程度上的确是这样,但是难道LInux就只有一种进程的东西么??答案肯定是否定的!下面咱们慢慢分析 说起Linux下的线程,的确不如windows下来的直接,windwos中进程和线程有着明确的区分,各自有自己代表的数据结构.操作API,进程享有资源和线程参加调度.一切都是

LINUX c++线程池框架

版权声明:原文地址及作者不详,如有侵权,请联系: 本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关.另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整线程池中线程的数量.文章的最后,我们给出一个简单示例程序,通过该示例程序,我们会发现,通过该线程池框架执行多线程任务是多么的简单. 为什么需要线程池 目前的大多数网络服务器,包括Web服务器.Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,

Linux下C语言多线程,网络通信简单聊天程序

原文:Linux下C语言多线程,网络通信简单聊天程序 功能描述:程序应用多线程技术,可是实现1对N进行网络通信聊天.但至今没想出合适的退出机制,除了用Ctr+C.出于演示目的,这里采用UNIX域协议(文件系统套接字),程序分为客户端和服务端.应用select函数来实现异步的读写操作. 先说一下服务端:首先先创建套接字,然后绑定,接下进入一个无限循环,用accept函数,接受“连接”请求,然后调用创建线程函数,创造新的线程,进入下一个循环.这样每当有一个新的“连接”被接受都会创建一个新的线程,实现

我对python线程池的理解

#!/usr/bin/env pythonfrom Queue import Queuefrom threading import Threadimport randomimport time def person(i,q):    while True:  #这个人一直处与可以接活干的状态        q.get()        print "Thread",i,"do_job"        time.sleep(random.randint(1,5))#每

Linux下线程pid和tid

#include <stdio.h> #include <pthread.h> #include <sys/types.h> #include <sys/syscall.h> struct message { int i; int j; }; void *hello(struct message *str) { printf("child, the tid=%lu, pid=%d\n",pthread_self(),syscall(SYS

linux下线程

linux下线程 线程与进程的关系: 之前转载的微信文章,进程与线程的差别已经说得比較清楚了.能够查看之前转载的文章.linux进程与线程的差别. 创建一个线程: #include<pthread.h> int pthread_creat(pthread_t * thread,pthread_attr_t * attr,void *(*stat_routine)(void *),void *arg); 惯例先写上函数原型: 參数:第一个參数pthread_t * thread 是一个指针,当进

Linux平台下线程池的原理及实现

转自:http://blog.csdn.net/lmh12506/article/details/7753952 前段时间在github上开了个库,准备实现自己的线程池的,因为换工作的事,一直也没有实现,参考这篇文章准备着手实现一下. 什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了.如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了. 下面是Linu