Linux平台下线程池的原理及实现
转自:http://blog.csdn.net/lmh12506/article/details/7753952
前段时间在github上开了个库,准备实现自己的线程池的,因为换工作的事,一直也没有实现,参考这篇文章准备着手实现一下。
什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。
下面是Linux系统下用C语言创建的一个线程池。线程池会维护一个任务链表(每个CThread_worker结构就是一个任务)。
pool_init()函数预先创建好max_thread_num个线程,每个线程执thread_routine ()函数。该函数中
- while (pool->cur_queue_size == 0)
- {
- pthread_cond_wait (&(pool->queue_ready),&(pool->queue_lock));
- }
表示如果任务链表中没有任务,则该线程出于阻塞等待状态。否则从队列中取出任务并执行。
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);
- 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;
- }
将上述所有代码放入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平台下线程池的原理及实现的更多相关文章
- linux平台下防火墙iptables原理(转)
原文地址:http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646466.html iptables简介 netfilter/iptables( ...
- linux平台下防火墙iptables原理
iptables简单介绍 netfilter/iptables(简称为iptables)组成Linux平台下的包过滤防火墙,与大多数的Linux软件一样,这个包过滤防火墙是免费的,它能够取代昂贵的商业 ...
- Thrift在Windows及Linux平台下的安装和使用示例
本文章也同时发表在个人博客Thrift在Windows及Linux平台下的安装和使用示例上. thrift介绍 Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的R ...
- Java线程池的原理及几类线程池的介绍
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
- Python 线程池的原理和实现及subprocess模块
最近由于项目需要一个与linux shell交互的多线程程序,需要用python实现,之前从没接触过python,这次匆匆忙忙的使用python,发现python确实语法非常简单,功能非常强大,因为自 ...
- 基于Linux/C++简单线程池的实现
我们知道Java语言对于多线程的支持十分丰富,JDK本身提供了很多性能优良的库,包括ThreadPoolExecutor和ScheduleThreadPoolExecutor等.C++11中的STL也 ...
- Java线程池实现原理及其在美团业务中的实践
本文转载自Java线程池实现原理及其在美团业务中的实践 导语 随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流.使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器.J.U.C提供 ...
- 转:一个跨WINDOWS LINUX平台的线程类
来源:http://blog.csdn.net/dengxu11/article/details/7232681 继Windows下实现一个CThread封装类之后,这里我再实现一个跨WINDOWS ...
- Java 线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
随机推荐
- 事件监听addEventListener()和removeEventListener() ---------1
一直想写一个原生事件监听的记录,方便以后看,不愿意写主要是事件监听的单词太长,记起来好难记每次都要查,这次把知道的写完了,来这里查好了,以后要是理解的更透彻了,就再补全好了 首先,DOM0级事件和DO ...
- Python学习总结18:函数 参数篇
1. 判断函数是否可调用 >>> import math >>> x = 1 >>> y = math.sqrt >>> cal ...
- ofbiz进击 第一节。 新建自己的webapp项目
创建一个webapp的过程更新下来项目(直接从svn上面切下来就好),要先ant clean 下,然后在重新ant下.一: start sheel here :ant create-component ...
- c++之路进阶——bzoj3343(教主的魔法)
F.A.Qs Home Discuss ProblemSet Status Ranklist Contest ModifyUser gryz2016 Logout 捐赠本站 Notice:由于本OJ ...
- c++之路起航——指针
c++一阶指针 定义 存储类型名 数据类型 * 指针变量名: Eg:int *a://定义了一个指向整型的指针 a: 指针使用方法 int a,*b; b=&a;//表明将a的地址赋值给b: ...
- (转)【ASP.NET开发】获取客户端IP地址 via C#
[ASP.NET开发]获取客户端IP地址 via C# 说明:本文中的内容是我综合博客园上的博文和MSDN讨论区的资料,再通过自己的实际测试而得来,属于自己原创的内容说实话很少,写这一篇是为了记录自己 ...
- oracle的例程
oracle只有在具备sysoper和sysdba权限下才能启动和关闭例程 关闭例程: --正常关闭(等待当前连接的所有用户与数据库断开) shutdown normal; --立即关闭(回退活动的事 ...
- js同步访问后台资源
$.ajax( { type : 'post', url : url, data : data, async : false,//false代表只有在等待ajax执行完毕后才执行window. ...
- 【crunch bang】中文美化
原文连接:http://edyfox.codecarver.org/html/debian_testing_chinese.html 中文字体美化是个很讨厌的事情,无数初学者在这里面浪费了无数时间,做 ...
- dataTabel转成dataview插入列后排序
if (!string.IsNullOrEmpty(strQuyu) && !string.IsNullOrEmpty(strZuhao)) { string[] param = { ...