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

下面是Linux系统下用C语言创建的一个线程池。线程池会维护一个任务链表(每个CThread_worker结构就是一个任务)。

pool_init()函数预先创建好max_thread_num个线程,每个线程执行thread_routine ()函数。该函数中

while (pool->cur_queue_size == )
{
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 = ; pool->shutdown = ; pool->threadid =
(pthread_t *) malloc (max_thread_num * sizeof (pthread_t));
int i = ;
for (i = ; 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 ;
} /*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int pool_destroy ()
{
if (pool->shutdown)
return -;/*防止两次调用*/
pool->shutdown = ; /*唤醒所有等待线程,线程池要销毁了*/
pthread_cond_broadcast (&(pool->queue_ready)); /*阻塞等待线程退出,否则就成僵尸了*/
int i;
for (i = ; 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 ;
} void * thread_routine (void *arg)
{
printf ("starting thread 0x%x\n", pthread_self ());
while ()
{
pthread_mutex_lock (&(pool->queue_lock));
/*如果等待队列为0并且不销毁线程池,则处于阻塞状态; 注意
pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
while (pool->cur_queue_size == && !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 != );
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 ();/*休息一秒,延长任务的执行时间*/
return NULL;
} int main (int argc, char **argv)
{
pool_init ();/*线程池中最多三个活动线程*/ /*连续向池中投入10个任务*/
int *workingnum = (int *) malloc (sizeof (int) * );
int i;
for (i = ; i < ; i++)
{
workingnum[i] = i;
pool_add_worker (myprocess, &workingnum[i]);
}
/*等待所有任务完成*/
sleep ();
/*销毁线程池*/
pool_destroy (); free (workingnum);
return ;
}

将上述所有代码放入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下简单线程池的实现

    大多数的网络服务器,包括Web服务器都具有一个特点,就是单位时间内必须处理数目巨大的连接请求,但是处理时间却是比较短的.在传统的多线程服务器模型中是这样实现的:一旦有个服务请求到达,就创建一个新的服务 ...

  2. linux下的线程池

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

  3. Linux下简易线程池

    线程池简介 线程池是可以用来在后台执行多个任务的线程集合. 这使主线程可以自由地异步执行其他任务.线程池通常用于服务器应用程序. 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会 ...

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

    线程池:简单地说,线程池 就是预先创建好一批线程,方便.快速地处理收到的业务.比起传统的到来一个任务,即时创建一个线程来处理,节省了线程的创建和回收的开销,响应更快,效率更高. 在linux中,使用的 ...

  5. 分享一个自制的 .net线程池

    扯淡 由于项目需求,需要开发一些程序去爬取一些网站的信息,算是小爬虫程序吧.爬网页这东西是要经过网络传输,如果程序运行起来串行执行请求爬取,会很慢,我想没人会这样做.为了提高爬取效率,必须使用多线程并 ...

  6. Linux中epoll+线程池实现高并发

    服务器并发模型通常可分为单线程和多线程模型,这里的线程通常是指“I/O线程”,即负责I/O操作,协调分配任务的“管理线程”,而实际的请求和任务通常交由所谓“工作者线程”处理.通常多线程模型下,每个线程 ...

  7. Linux网络通信(线程池和线程池版本的服务器代码)

    线程池 介绍 线程池: 一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的 ...

  8. 推荐一个linux下的web压力测试工具神器webbench

    推荐一个linux下的web压力测试工具神器webbench2014-04-30 09:35:29   来源:   评论:0 点击:880 用多了apache的ab工具之后你就会发现ab存在很多问题, ...

  9. Linux下查看线程数的几种方法汇总

    Linux下查看线程数的几种方法汇总 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Linux下查看某个进程的线程数量 pstree命令以树状图显示进程间的关系(display ...

随机推荐

  1. Informatica 常用组件Source Qualifier之八 会话前和会话后 SQL

      可以在源限定符转换的"属性"选项卡中添加会话前和会话后 SQL 命令.您可能要使用会话前 SQL 以在会话开始时将时间标识行写入源表. PowerCenter 在读取源之前对源 ...

  2. 记录C#错误日志工具

    在编程过程中,我们经常会用try...catch处理可能出错的代码块.如果程序出现错误,则直接show出错误信息. 当然,大型的系统都有错误日志处理模块,用数据库记录错误日志信息,有相应的写入错误日志 ...

  3. Android之批量加载图片OOM问题解决方案

    一.OOM问题出现的场景和原因 一个好的app总少不了精美的图片,所以Android开发中图片的加载总是避免不了的,而在加载图片过程中,如果处理不当则会出现OOM的问题.那么如何彻底解决这个问题呢?本 ...

  4. android 随手记 摄像头录像

    1 xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:androi ...

  5. 如何在Linux上安装服务器管理软件Cockpit

    Cockpit 是一个自由开源的服务器管理软件,使得我们可以通过它好看的 Web 前端界面轻松地管理我们的 GNU/Linux 服务器,非常轻量级,Web 界面也非常简单易用. Cockpit 使得 ...

  6. Android直播实现 Android端推流、播放

    最近想实现一个Android直播,但是对于这方面的资料都比较零碎,一开始是打算用ffmpeg来实现编码推流,在搜集资料期间,找到了几个强大的开源库,直接避免了jni的代码,集成后只用少量的java代码 ...

  7. Discuz常见小问题-如何修改自己发布的帖子

    在发布的帖子的下方就有编辑的按钮,可以直接点击进去编辑

  8. HDU 2178.猜数字【分析能力练习】【读题能力练习】【8月10】

    猜数字 Problem Description A有1数m.B来猜.B每猜一次,A就说"太大"."太小"或"对了" . 问B猜n次能够猜到的 ...

  9. Octave中plot函数的用法

    octave:14> help plot'plot' is a function from the file C:\Octave\Octave3.6.4_gcc4.6.2\share\octav ...

  10. PHP 自学之路-----XML编程(Dom技术)

    上一节,讲了Xml文件基本语法及元素,实体及Dtd约束技术,下面就正式进入PHP的Xml编程 使用PHP技术对Xml文件进行操作 常用的有以下三种技术: 1.PHP dom 2.PHP结合XPath操 ...