原文转载自:https://blog.csdn.net/hubi0952/article/details/8045094

1.线程池基本原理

在传统服务器结构中, 常是 有一个总的 监听线程监听有没有新的用户连接服务器, 每当有一个新的 用户进入, 服务器就开启一个新的线程用户处理这 个用户的数据包。这个线程只服务于这个用户 , 当 用户与服务器端关闭连接以后, 服务器端销毁这个线程。然而频繁地开辟与销毁线程极大地占用了系统的资源。而且在大量用户的情况下, 系统为了开辟和销毁线程将浪费大量的时间和资源。线程池提供了一个解决外部大量用户与服务器有限资源的矛盾, 线程池和传统的一个用户对应一 个线程的处理方法不同, 它的基本思想就是在程序 开始时就在内存中开辟一些线程, 线程的数目是 固定的,他们独自形成一个类, 屏蔽了对外的操作, 而服务器只需要将数据包交给线程池就可以了。当有新的客户请求到达时 , 不是新创建一个线程为其服务 , 而是从“池子”中选择一个空闲的线程为新的客户请求服务 ,服务完毕后 , 线程进入空闲线程池中。如果没有线程空闲 的 话, 就 将 数 据 包 暂 时 积 累 , 等 待 线 程 池 内 有 线 程空闲以后再进行处理。通过对多个任务重用已经存在的线程对象 , 降低了对线程对象创建和销毁的开销。当客户请求 时 , 线程对象 已 经 存 在 , 可 以 提 高 请 求 的响应时间 , 从而整体地提高了系统服务的表现。

一般来说实现一个线程池主要包括以下几个组成部分:

1)线程管理器:用于创建并管理线程池。

2)工作线程:线程池中实际执行任务的线程。在初始化线程时会预先创建好固定数目的线程在池中,这些初始化的线程一般处于空闲状态,一般不占用CPU,占用较小的内存空间。

3)任务接口:每个任务必须实现的接口,当线程池的任务队列中有可执行任务时,被空闲的工作线程调去执行(线程的闲与忙是通过互斥量实现的,跟前面文章中的设置标志位差不多),把任务抽象出来形成接口,可以做到线程池与具体的任务无关。

4)任务队列:用来存放没有处理的任务,提供一种缓冲机制,实现这种结构有好几种方法,常用的是队列,主要运用先进先出原理,另外一种是链表之类的数据结构,可以动态的为它分配内存空间,应用中比较灵活,下文中就是用到的链表。

下面的不在赘述百度《线程池技术在并发服务器中的应用》写的非常详细!

转自:http://blog.csdn.net/zouxinfox/article/details/3560891

什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。

下面是Linux系统下用C语言创建的一个线程池。线程池会维护一个任务链表(每个CThread_worker结构就是一个任务)。
    pool_init()函数预先创建好max_thread_num个线程,每个线程执thread_routine ()函数。该函数中

  1. while (pool->cur_queue_size == 0)
  2. {
  3. pthread_cond_wait (&(pool->queue_ready),&(pool->queue_lock));
  4. }

表示如果任务链表中没有任务,则该线程出于阻塞等待状态。否则从队列中取出任务并执行。
    
    pool_add_worker()函数向线程池的任务链表中加入一个任务,加入后通过调用pthread_cond_signal (&(pool->queue_ready))唤醒一个出于阻塞状态的线程(如果有的话)。
    
    pool_destroy ()函数用于销毁线程池,线程池任务链表中的任务不会再被执行,但是正在运行的线程会一直把任务运行完后再退出。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <assert.h>/*
*线程池里所有运行和等待的任务都是一个CThread_worker
*由于所有任务都在链表里,所以是一个链表结构
*/
typedef struct worker
{/*回调函数,任务运行时会调用此函数,注意也可声明成其它形式*/void *(*process) (void *arg);void *arg;/*回调函数的参数*/struct worker *next;} CThread_worker;/*线程池结构*/
typedef struct
{pthread_mutex_t queue_lock;pthread_cond_t queue_ready;/*链表结构,线程池中所有等待任务*/CThread_worker *queue_head;/*是否销毁线程池*/int shutdown;pthread_t *threadid;/*线程池中允许的活动线程数目*/int max_thread_num;/*当前等待队列的任务数目*/int cur_queue_size;} CThread_pool;int pool_add_worker (void *(*process) (void *arg), void *arg);
void *thread_routine (void *arg);//share resource
static CThread_pool *pool = NULL;
void
pool_init (int max_thread_num)
{pool = (CThread_pool *) malloc (sizeof (CThread_pool));pthread_mutex_init (&(pool->queue_lock), NULL);pthread_cond_init (&(pool->queue_ready), NULL);pool->queue_head = NULL;pool->max_thread_num = max_thread_num;pool->cur_queue_size = 0;pool->shutdown = 0;pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t));int i = 0;for (i = 0; i < max_thread_num; i++){ pthread_create (&(pool->threadid[i]), NULL, thread_routine,NULL);}
}/*向线程池中加入任务*/
int
pool_add_worker (void *(*process) (void *arg), void *arg)
{/*构造一个新任务*/CThread_worker *newworker = (CThread_worker *) malloc (sizeof (CThread_worker));newworker->process = process;newworker->arg = arg;newworker->next = NULL;/*别忘置空*/pthread_mutex_lock (&(pool->queue_lock));/*将任务加入到等待队列中*/CThread_worker *member = pool->queue_head;if (member != NULL){while (member->next != NULL)member = member->next;member->next = newworker;}else{pool->queue_head = newworker;}assert (pool->queue_head != NULL);pool->cur_queue_size++;pthread_mutex_unlock (&(pool->queue_lock));/*好了,等待队列中有任务了,唤醒一个等待线程;注意如果所有线程都在忙碌,这句没有任何作用*/pthread_cond_signal (&(pool->queue_ready));return 0;
}/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int
pool_destroy ()
{if (pool->shutdown)return -1;/*防止两次调用*/pool->shutdown = 1;/*唤醒所有等待线程,线程池要销毁了*/pthread_cond_broadcast (&(pool->queue_ready));/*阻塞等待线程退出,否则就成僵尸了*/int i;for (i = 0; i < pool->max_thread_num; i++)pthread_join (pool->threadid[i], NULL);free (pool->threadid);/*销毁等待队列*/CThread_worker *head = NULL;while (pool->queue_head != NULL){head = pool->queue_head;pool->queue_head = pool->queue_head->next;free (head);}/*条件变量和互斥量也别忘了销毁*/pthread_mutex_destroy(&(pool->queue_lock));pthread_cond_destroy(&(pool->queue_ready));free (pool);/*销毁后指针置空是个好习惯*/pool=NULL;return 0;
}void *
thread_routine (void *arg)
{printf ("starting thread 0x%x\n", pthread_self ());while (1){pthread_mutex_lock (&(pool->queue_lock));/*如果等待队列为0并且不销毁线程池,则处于阻塞状态; 注意pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/while (pool->cur_queue_size == 0 && !pool->shutdown){printf ("thread 0x%x is waiting\n", pthread_self ());pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock));}/*线程池要销毁了*/if (pool->shutdown){/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/pthread_mutex_unlock (&(pool->queue_lock));printf ("thread 0x%x will exit\n", pthread_self ());pthread_exit (NULL);}printf ("thread 0x%x is starting to work\n", pthread_self ());/*assert是调试的好帮手*/assert (pool->cur_queue_size != 0);assert (pool->queue_head != NULL);/*等待队列长度减去1,并取出链表中的头元素*/pool->cur_queue_size--;CThread_worker *worker = pool->queue_head;pool->queue_head = worker->next;pthread_mutex_unlock (&(pool->queue_lock));/*调用回调函数,执行任务*/(*(worker->process)) (worker->arg);free (worker);worker = NULL;}/*这一句应该是不可达的*/pthread_exit (NULL);
}//    下面是测试代码void *
myprocess (void *arg)
{printf ("threadid is 0x%x, working on task %d\n", pthread_self (),*(int *) arg);sleep (1);/*休息一秒,延长任务的执行时间*/return NULL;
}int
main (int argc, char **argv)
{pool_init (3);/*线程池中最多三个活动线程*//*连续向池中投入10个任务*/int *workingnum = (int *) malloc (sizeof (int) * 10);int i;for (i = 0; i < 10; i++){workingnum[i] = i;pool_add_worker (myprocess, &workingnum[i]);}/*等待所有任务完成*/sleep (5);/*销毁线程池*/pool_destroy ();free (workingnum);return 0;
}

LINUX 下C实现线程池《转载》相关推荐

  1. Linux下C语言线程池的实现(1)

    http://hi.baidu.com/lingiloveyou/blog/item/21e57cf3322a6b40342accc7.html 什么时候需要创建线程池呢?简单的说,如果一个应用需要频 ...

  2. linux下c++版本线程池的实现

    该版本的是来源c语言版本的修改,只是对其进行了封装,这里不过得解释原理,大家可以看这篇文章 下面将直接上代码,同时这里需要主要的是使用的线程函数是linux自带的不是c++的自带线程函数,后续会使用c ...

  3. C#多线程学习(四) 多线程的自动管理(线程池) (转载系列)——继续搜索引擎研究...

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  4. Multi-thread--Windows和Linux下通用的线程接口

    对于多线程开发,Linux下有pthread线程库,使用起来比较方便,而Windows没有,对于涉及到多线程的跨平台代码开发,会带来不便.这里参考网络上的一些文章,整理了在Windows和Linux下 ...

  5. Windows和Linux下通用的线程接口

    对于多线程开发,Linux下有pthread线程库,使用起来比较方便,而Windows没有,对于涉及到多线程的跨平台代码开发,会带来不便.这里参考网络上的一些文章,整理了在Windows和Linux下 ...

  6. Windows/Linux下获取当前线程的ID号

    序 在多线程场合,为了方便跟踪线程的运行状态,往往需要在程序中添加打印当前线程ID号的功能. 1. Linux下打印当前线程ID pthread_t pthread_self() 2. Windows ...

  7. linux下c语言线程传参数,【linux】C语言多线程中运行线程池,在线程池中运行线程池,,传递的结构体参数值为空/NULL/0...

    C语言多线程中运行线程池,在线程池中运行线程池,,传递的结构体参数值为空/NULL/0 本贴问题,之前已经提问过一次,当时已经解决了,原贴在这里https://segmentfault.com/q/1 ...

  8. linux进程同步问题,关于LINUX下进程和线程对文件的同步问题,请高手来看看!!!...

    不知道大家有没有碰到过这样的问题,即在LINUX下的程序有时候会碰到多个进程访问同一个文件, 以及一个进程的不同线程对一个文件的同步问题,我尝试用了系统提供的fcntl函数来给文件加锁, 但是这只是一 ...

  9. Linux下history命令详解---转载

    Linux下History命令主要用于显示历史指令记录内容, 下达历史纪录中的指令 . >History命令语法: [www.linuxidc.com@linux]# history [n] [ ...

最新文章

  1. php减少损耗的方法之一 缓存对象
  2. 一文读懂卷积神经网络CNN(学习笔记)
  3. c语言回调函数_C语言学习第26篇---函数与指针分析 回调函数
  4. c++ 函数过长 拆分_实用EXECL办公函数 【多条件排名】
  5. Linux项目自动化构建工具 make/Makefile
  6. 抖音txt表白html,抖音txt弹窗表白整蛊怎么弄 抖音表白撩妹套路弹窗设置教程
  7. vue 带全选和多选的表格怎么写_EXCEL五分钟,批量制作带照片的工地出入证
  8. Spring 3.1配置文件和Tomcat配置
  9. 移动spa商城优化记(一)---首屏优化篇
  10. oracle java存储过程返回值_java程序调用Oracle 存储过程 获取返回值(无返回,非结果集,结果集)...
  11. 【Flink】flink sql的并行度怎么单独设置
  12. vmware-vmx.exe无法结束进程_孤儿进程与僵尸进程产生原理分析,以及终极解决方案案例实现...
  13. android-activity生命周期方法
  14. SQL Profile (总结4)--使用演示示例
  15. MOQL—过滤器(Filter)
  16. pku 2251 Dungeon Master
  17. 实验平均梯度与图像的模糊程度(matlab 代码)
  18. 交换机端口vlan模式
  19. isalpha、isalnum、islower、isupper等字符函数
  20. QT之隐藏任务栏图标

热门文章

  1. 小程序连接优声云打印机流程
  2. Win32 IME 编程心得【转】
  3. win32下的OpenGL绘图环境框架
  4. 计算机太卡了怎么解决,电脑太卡怎么办最有效
  5. oracle如何恢复删除的表
  6. 【山无遮,海无拦】LeetCode题集 线性枚举之最值算法
  7. 测试一个教室和一个椅子
  8. E - Help Hanzo(LightOJ 1197)
  9. primeng dropdown ngmodel 选择项初始化
  10. 芝诺志愿者走进养老院送温暖