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 ()函数用于销毁线程池,线程池任务链表中的任务不会再被执行,但是正在运行的线程会一直把任务运行完后再退出。

[cpp] view plain copy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <pthread.h>
  6. #include <assert.h>
  7. /*
  8. *线程池里所有运行和等待的任务都是一个CThread_worker
  9. *由于所有任务都在链表里,所以是一个链表结构
  10. */
  11. typedef struct worker
  12. {
  13. /*回调函数,任务运行时会调用此函数,注意也可声明成其它形式*/
  14. void *(*process) (void *arg);
  15. void *arg;/*回调函数的参数*/
  16. struct worker *next;
  17. } CThread_worker;
  18. /*线程池结构*/
  19. typedef struct
  20. {
  21. pthread_mutex_t queue_lock;
  22. pthread_cond_t queue_ready;
  23. /*链表结构,线程池中所有等待任务*/
  24. CThread_worker *queue_head;
  25. /*是否销毁线程池*/
  26. int shutdown;
  27. pthread_t *threadid;
  28. /*线程池中允许的活动线程数目*/
  29. int max_thread_num;
  30. /*当前等待队列的任务数目*/
  31. int cur_queue_size;
  32. } CThread_pool;
  33. int pool_add_worker (void *(*process) (void *arg), void *arg);
  34. void *thread_routine (void *arg);
  35. //share resource
  36. static CThread_pool *pool = NULL;
  37. void
  38. pool_init (int max_thread_num)
  39. {
  40. pool = (CThread_pool *) malloc (sizeof (CThread_pool));
  41. pthread_mutex_init (&(pool->queue_lock), NULL);
  42. pthread_cond_init (&(pool->queue_ready), NULL);
  43. pool->queue_head = NULL;
  44. pool->max_thread_num = max_thread_num;
  45. pool->cur_queue_size = 0;
  46. pool->shutdown = 0;
  47. pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t));
  48. int i = 0;
  49. for (i = 0; i < max_thread_num; i++)
  50. {
  51. pthread_create (&(pool->threadid[i]), NULL, thread_routine,NULL);
  52. }
  53. }
  54. /*向线程池中加入任务*/
  55. int
  56. pool_add_worker (void *(*process) (void *arg), void *arg)
  57. {
  58. /*构造一个新任务*/
  59. CThread_worker *newworker = (CThread_worker *) malloc (sizeof (CThread_worker));
  60. newworker->process = process;
  61. newworker->arg = arg;
  62. newworker->next = NULL;/*别忘置空*/
  63. pthread_mutex_lock (&(pool->queue_lock));
  64. /*将任务加入到等待队列中*/
  65. CThread_worker *member = pool->queue_head;
  66. if (member != NULL)
  67. {
  68. while (member->next != NULL)
  69. member = member->next;
  70. member->next = newworker;
  71. }
  72. else
  73. {
  74. pool->queue_head = newworker;
  75. }
  76. assert (pool->queue_head != NULL);
  77. pool->cur_queue_size++;
  78. pthread_mutex_unlock (&(pool->queue_lock));
  79. /*好了,等待队列中有任务了,唤醒一个等待线程;
  80. 注意如果所有线程都在忙碌,这句没有任何作用*/
  81. pthread_cond_signal (&(pool->queue_ready));
  82. return 0;
  83. }
  84. /*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
  85. 把任务运行完后再退出*/
  86. int
  87. pool_destroy ()
  88. {
  89. if (pool->shutdown)
  90. return -1;/*防止两次调用*/
  91. pool->shutdown = 1;
  92. /*唤醒所有等待线程,线程池要销毁了*/
  93. pthread_cond_broadcast (&(pool->queue_ready));
  94. /*阻塞等待线程退出,否则就成僵尸了*/
  95. int i;
  96. for (i = 0; i < pool->max_thread_num; i++)
  97. pthread_join (pool->threadid[i], NULL);
  98. free (pool->threadid);
  99. /*销毁等待队列*/
  100. CThread_worker *head = NULL;
  101. while (pool->queue_head != NULL)
  102. {
  103. head = pool->queue_head;
  104. pool->queue_head = pool->queue_head->next;
  105. free (head);
  106. }
  107. /*条件变量和互斥量也别忘了销毁*/
  108. pthread_mutex_destroy(&(pool->queue_lock));
  109. pthread_cond_destroy(&(pool->queue_ready));
  110. free (pool);
  111. /*销毁后指针置空是个好习惯*/
  112. pool=NULL;
  113. return 0;
  114. }
  115. void *
  116. thread_routine (void *arg)
  117. {
  118. printf ("starting thread 0x%x\n", pthread_self ());
  119. while (1)
  120. {
  121. pthread_mutex_lock (&(pool->queue_lock));
  122. /*如果等待队列为0并且不销毁线程池,则处于阻塞状态; 注意
  123. pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
  124. while (pool->cur_queue_size == 0 && !pool->shutdown)
  125. {
  126. printf ("thread 0x%x is waiting\n", pthread_self ());
  127. pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock));
  128. }
  129. /*线程池要销毁了*/
  130. if (pool->shutdown)
  131. {
  132. /*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
  133. pthread_mutex_unlock (&(pool->queue_lock));
  134. printf ("thread 0x%x will exit\n", pthread_self ());
  135. pthread_exit (NULL);
  136. }
  137. printf ("thread 0x%x is starting to work\n", pthread_self ());
  138. /*assert是调试的好帮手*/
  139. assert (pool->cur_queue_size != 0);
  140. assert (pool->queue_head != NULL);
  141. /*等待队列长度减去1,并取出链表中的头元素*/
  142. pool->cur_queue_size--;
  143. CThread_worker *worker = pool->queue_head;
  144. pool->queue_head = worker->next;
  145. pthread_mutex_unlock (&(pool->queue_lock));
  146. /*调用回调函数,执行任务*/
  147. (*(worker->process)) (worker->arg);
  148. free (worker);
  149. worker = NULL;
  150. }
  151. /*这一句应该是不可达的*/
  152. pthread_exit (NULL);
  153. }
  154. //    下面是测试代码
  155. void *
  156. myprocess (void *arg)
  157. {
  158. printf ("threadid is 0x%x, working on task %d\n", pthread_self (),*(int *) arg);
  159. sleep (1);/*休息一秒,延长任务的执行时间*/
  160. return NULL;
  161. }
  162. int
  163. main (int argc, char **argv)
  164. {
  165. pool_init (3);/*线程池中最多三个活动线程*/
  166. /*连续向池中投入10个任务*/
  167. int *workingnum = (int *) malloc (sizeof (int) * 10);
  168. int i;
  169. for (i = 0; i < 10; i++)
  170. {
  171. workingnum[i] = i;
  172. pool_add_worker (myprocess, &workingnum[i]);
  173. }
  174. /*等待所有任务完成*/
  175. sleep (5);
  176. /*销毁线程池*/
  177. pool_destroy ();
  178. free (workingnum);
  179. return 0;
  180. }

将上述所有代码放入threadpool.c文件中,
在Linux输入编译命令
$ gcc -o threadpool threadpool.c -lpthread

以下是运行结果
starting thread 0xb7df6b90
thread 0xb7df6b90 is waiting
starting thread 0xb75f5b90
thread 0xb75f5b90 is waiting
starting thread 0xb6df4b90
thread 0xb6df4b90 is waiting
thread 0xb7df6b90 is starting to work
threadid is 0xb7df6b90, working on task 0
thread 0xb75f5b90 is starting to work
threadid is 0xb75f5b90, working on task 1
thread 0xb6df4b90 is starting to work
threadid is 0xb6df4b90, working on task 2
thread 0xb7df6b90 is starting to work
threadid is 0xb7df6b90, working on task 3
thread 0xb75f5b90 is starting to work
threadid is 0xb75f5b90, working on task 4
thread 0xb6df4b90 is starting to work
threadid is 0xb6df4b90, working on task 5
thread 0xb7df6b90 is starting to work
threadid is 0xb7df6b90, working on task 6
thread 0xb75f5b90 is starting to work
threadid is 0xb75f5b90, working on task 7
thread 0xb6df4b90 is starting to work
threadid is 0xb6df4b90, working on task 8
thread 0xb7df6b90 is starting to work
threadid is 0xb7df6b90, working on task 9
thread 0xb75f5b90 is waiting
thread 0xb6df4b90 is waiting
thread 0xb7df6b90 is waiting
thread 0xb75f5b90 will exit
thread 0xb6df4b90 will exit
thread 0xb7df6b90 will exit

一个Linux下C线程池的实现(转)相关推荐

  1. 一个Linux下C线程池的实现

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

  2. Linux下通用线程池的创建与使用

    Linux下通用线程池的创建与使用 本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关.另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整 ...

  3. Linux下简单线程池的实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

  4. linux进程池 自动增长,linux下C 线程池的原理讲解和代码实现(能自行伸缩扩展线程数)...

    什么线程池,为什么要使用线程池?下面是一个比喻. 阶段一.一个医院,每天面对成千上万的病人,处理方式是:来一个病人找来一个医生处理,处理完了医生也走了.当看病时间较短的时候,医生来去的时间,显得尤为费 ...

  5. Linux下C线程池的实现

    如何在嵌入式开发板上内置网页,通过多个客服端来同时访问呢?因为需要大量线程来处理网页的任务,为了避免频繁的申请释放线程所带来的开销,使用了线程池来操作! 源代码: //服务端监听客服端访问网页的请求 ...

  6. linux的创建线程池,Linux下通用线程池的创建与使用(上) (3)

    线程的状态可以分为四种,空闲.忙碌.挂起.终止(包括正常退出和非正常退出).由于目前Linux线程库不支持挂起操作,因此,我们的此处的挂起操作类似于暂停.如果线程创建后不想立即执行任务,那么我们可以将 ...

  7. Linux下的线程池源代码请到下面的链接下载

    http://www.namipan.com/d/linuxpool.rar/fcdea853d6fe67c7015f89ca8c6faca0778a86278d600c00

  8. Linux怎样给线程取名字,Linux下指定线程的名字

    为了能方便的区分一个进程中的每个线程,可以通过prctl()给每个线程取个名字.这样在会创建多个线程的程序执行过程中,就能知道一个pid或tid对应的是哪个线程,对调试程序有一定帮助. prctl是个 ...

  9. 【C++学习】 基于Linux/C++简单线程池的实现

    [C++学习] 基于Linux/C++简单线程池的实现 转载自:https://www.cnblogs.com/alwayswangzi/p/7138154.html 我们知道Java语言对于多线程的 ...

最新文章

  1. 无参数的构造函数如何声明对象?
  2. C++ #pragma comment语法(预处理指令)
  3. 的写法_横、竖的写法
  4. java中Runnable和Callable的区别
  5. 前端协商缓存强缓存如何使用_前端强缓存和协商缓存
  6. python正则匹配html标签_Python正则获取、过滤或者替换HTML标签的方法
  7. (转)淘淘商城系列——使用JsonView来格式化json字符串
  8. Mac 入门教程:如何更改你的 Mac 设备名称
  9. sqlserver2008的数据库自动备份方法(转载)
  10. 人的大脑是如何识别运动物体
  11. Premiere视频剪辑软件的破解和安装
  12. mysql中转换日期格式,MySQL日期格式转换
  13. python编写贪吃蛇大战_python实现贪吃蛇双人大战
  14. idea中maven打包报错:Compilation failure: Compilation failure
  15. ACK((Acknowledge character)应答机制(kafka)
  16. 利用 ffmpeg swr_convert重采样
  17. 自动化部署工具OneinStack:从入坑到出坑
  18. [C++贪心习题]压缩歌曲
  19. 从环形图出发,打造高效数据分析流程
  20. java 开源 cms FreeCMS1.7发布

热门文章

  1. java中HashMap的用法
  2. APIO2015 酱油记
  3. linux操作系统好吗_国内可以通过安卓+termux打造出适用手机平板和电脑全平台最好的操作系统...
  4. java取非_java运算符 与()、非(~)、或(|)、异或(^)
  5. android手机最低内存,原神手机端需要哪些配置 手机端最低配置要求介绍
  6. linux redis安装使用,linux安装redis
  7. stm32f103r6最小系统原理图_超强PCB布线设计经验谈附原理图
  8. oracle锁表会话超时时间,ORACLE快速彻底Kill掉的会话,防止锁表
  9. Linux 安装之U盘引导
  10. MySQL 8.0.22执行器源码分析HashJoin —— 一些初始化函数的细节步骤