讨论QQ群:135202158

本文技术参考了sourceforge项目c thread pool,链接:http://sourceforge.net/projects/cthpool/

线程池如上一篇随笔(http://www.cnblogs.com/zzqcn/p/3585003.html)提到的内存池一样,也是一种池化策略,在启动时(或者更高级的,运行时按一定策略分配)预先开启N个线程,当没有工作要做时,这些线程处于睡眠中;一旦有工作加入工作队列,其中的某些线程就会醒来,处理这些工作,完成后继续睡眠 。

要实现线程池(只针对本文的简单实现而言),应设计和构建3样东西:

  1. 含N个线程的线程组
  2. 工作队列
  3. 工作线程例程

线程组和工作队列表示如下:

/*
* Threads:
*
* +----------+----------+------+------------+
* | thread 0 | thread 1 | .... | thread n-1 |
* +----------+----------+------+------------+
*
* Job Queue:
*
* back front
* | |
* v v
* +-------+ +-------+ +-------+
* | job 0 | -> | job 1 | -> ... -> | job x |
* +-------+ +-------+ +-------+
*
*/

线程组可以用普通数组或者动态分配的数组实现,维数就是池中线程数量,存放的其实是线程ID。工作队列可以直接用C++ queue容器实现。

工作线程例程(线程函数)的大致执行流程如下图所示:

/*
*
* Each Thread Routine:
* Job-Queue
* | ...
* v |
* +-------+ +---------+ EnQueue
* +---> | sleep | (No job) | new job | <--------- Client
* | +-------+ +---------+
* | | |
* | | DeQueue +---------+
* | + <----------- | new job |
* | | +---------+
* | v
* | +---------+
* | | do work |
* | +---------+
* | |
* | |
* +----<----+
*
*/

工作队列中没有工作时它就睡眠 ,有工作时苏醒,从队列首部取出(&删除)一个工作,然后开始执行。

另外,我们还需要一个互斥锁L和一个计数信号量S,互斥锁用来同步工作队列的增删操作,计数信号量用来对工作队列中的工作数量进行记录。工作线程会一直等待S,直到它大于0。

下面给出完整代码。

1. threadpool.h

 /*
* Linux线程池的简单实现.
* Author: 赵子清
* Blog: http://www.cnblogs.com/zzqcn
*
**/ #ifndef __THREADPOOL_H__
#define __THREADPOOL_H__ #include <semaphore.h>
#include <pthread.h>
#include <queue> #define DLPTP_MAX_THREADS 1024 struct tp_job_t
{
void (*work) (void*);
void* arg;
}; struct tp_threadpool_t
{
pthread_t* threads;
size_t nthreads;
std::queue<tp_job_t> jobs;
sem_t njobs;
pthread_mutex_t lock;
bool running;
}; tp_threadpool_t* tp_init(size_t _nthreads);
int tp_deinit(tp_threadpool_t* _ptp);
void* tp_worker(void* _ptp);
int tp_add_job(tp_threadpool_t* _ptp, void (*_work)(void*), void* _arg); #endif

2. threadpool.cpp

 /*
* Linux线程池的简单实现.
* Author: 赵子清
* Blog: http://www.cnblogs.com/zzqcn
*
**/ #include "threadpool.h" tp_threadpool_t* tp_init(size_t _nthreads)
{
if(_nthreads < || _nthreads > DLPTP_MAX_THREADS)
return NULL; int err = ;
tp_threadpool_t* ret = NULL;
size_t i, j; ret = new tp_threadpool_t;
if(NULL == ret)
return NULL;
ret->nthreads = _nthreads;
ret->threads = new pthread_t[_nthreads];
if(NULL == ret->threads)
{
delete ret;
return NULL;
}
ret->running = true; err = sem_init(&ret->njobs, , );
if(- == err)
{
delete[] ret->threads;
delete ret;
return NULL;
} err = pthread_mutex_init(&ret->lock, NULL);
if(err)
{
sem_destroy(&ret->njobs);
delete[] ret->threads;
delete ret;
return NULL;
} for(i=; i<_nthreads; ++i)
{
err = pthread_create(&ret->threads[i], NULL, tp_worker, (void*)ret);
if(err)
{
ret->running = false;
for(j=; j<i; ++j)
{
pthread_cancel(ret->threads[j]);
pthread_join(ret->threads[j], NULL);
}
pthread_mutex_destroy(&ret->lock);
sem_destroy(&ret->njobs);
delete[] ret->threads;
delete ret;
return NULL;
}
} return ret;
} int tp_deinit(tp_threadpool_t* _ptp)
{
if(NULL == _ptp)
return -; int err = ;
size_t i, j; // TODO: if now worker has job to handle, do something then exit
while(!_ptp->jobs.empty()); _ptp->running = false; for(i=; i<_ptp->nthreads; ++i)
{
err = sem_post(&_ptp->njobs); /* V, ++ */
if(err)
{
for(j=i; j<_ptp->nthreads; ++j)
pthread_cancel(_ptp->threads[j]);
break;
}
} for(i=; i<_ptp->nthreads; ++i)
pthread_join(_ptp->threads[i], NULL); pthread_mutex_destroy(&_ptp->lock);
sem_destroy(&_ptp->njobs); delete[] _ptp->threads; _ptp->threads = NULL;
delete _ptp; _ptp = NULL; return ;
} void* tp_worker(void* _ptp)
{
if(NULL == _ptp)
return NULL; tp_threadpool_t* p = (tp_threadpool_t*)_ptp; while(p->running)
{
sem_wait(&p->njobs); /* P, -- */ if(!p->running)
return NULL; void (*work) (void*);
void* arg;
tp_job_t job; pthread_mutex_lock(&p->lock); /* LOCK */ job = p->jobs.front();
work = job.work;
arg = job.arg;
p->jobs.pop(); pthread_mutex_unlock(&p->lock); /* UNLOCK */ work(arg);
} return NULL;
} int tp_add_job(tp_threadpool_t* _ptp, void (*_work)(void*), void* _arg)
{
if(NULL == _ptp || NULL == _work)
return -; tp_job_t job;
job.work = _work;
job.arg = _arg; pthread_mutex_lock(&_ptp->lock); /* LOCK */
_ptp->jobs.push(job);
sem_post(&_ptp->njobs); /* V, ++ */
pthread_mutex_unlock(&_ptp->lock); /* UNLOCK */ return ;
}

3. 测试程序main.cpp

 /*
* Linux线程池测试.
* Author: 赵子清
* Blog: http://www.cnblogs.com/zzqcn
*
**/ #include <unistd.h>
#include <stdio.h>
#include "threadpool.h" /* task 1 */
void task1(void* _arg)
{
printf("# Thread working: %u\n", (int)pthread_self());
printf(" Task 1 running..\n");
usleep();
} /* task 2 */
void task2(void* _arg)
{
printf("# Thread working: %u\n", (int)pthread_self());
printf(" Task 2 running.. ");
printf("%d\n", *((int*)_arg));
usleep();
} #define N_THREADS 4 int main(int argc, char** argv)
{
tp_threadpool_t* ptp = NULL;
int i; ptp = tp_init(N_THREADS);
if(NULL == ptp)
{
fprintf(stderr, "tp_init fail\n");
return -;
} int a = ;
for(i=; i<; ++i)
{
tp_add_job(ptp, task1, NULL);
tp_add_job(ptp, task2, (void*)&a);
} tp_deinit(ptp); return ;
}

线程池(Linux实现)的更多相关文章

  1. 线程池 ------ linux C实现

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

  2. 一个简单的linux线程池(转-wangchenxicool)

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

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

    转自:http://blog.csdn.net/lmh12506/article/details/7753952 前段时间在github上开了个库,准备实现自己的线程池的,因为换工作的事,一直也没有实 ...

  4. 高效线程池之无锁化实现(Linux C)

    from:http://blog.csdn.net/xhjcehust/article/details/45844901 笔者之前练手写过一个小的线程池版本(已上传至https://github.co ...

  5. Linux + C + Epoll实现高并发服务器(线程池 + 数据库连接池)(转)

    转自:http://blog.csdn.net/wuyuxing24/article/details/48758927 一, 背景 先说下我要实现的功能,server端一直在linux平台下面跑,当客 ...

  6. 在Linux下写一个线程池以及线程池的一些用法和注意点

    -->线程池介绍(大部分来自网络)  在这个部分,详细的介绍一下线程池的作用以及它的技术背景以及他提供的一些服务等.大部分内容来自我日常生活中在网络中学习到的一些概念性的东西. -->代码 ...

  7. Linux C 一个简单的线程池程序设计

    最近在学习linux下的编程,刚开始接触感觉有点复杂,今天把线程里比较重要的线程池程序重新理解梳理一下. 实现功能:创建一个线程池,该线程池包含若干个线程,以及一个任务队列,当有新的任务出现时,如果任 ...

  8. Linux多线程实践(9) --简单线程池的设计与实现

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

  9. Linux下简易线程池

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

  10. Linux C 实现一个简单的线程池

    线程池的定义 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如 ...

随机推荐

  1. 【总结整理】关于挪车和虚拟号的思考-转载v2ex

    https://www.baidu.com/link?url=A7wiF1JpOkT6Juo0nNHKcum0OiQsnRj-EZkQfjc3xB-noUeLy3HEY-4plbFmPmuJ& ...

  2. mysql 求2个坐标之间的距离

    CREATE DEFINER=`root`@`%` FUNCTION `f_GetDistance`(lng1 DOUBLE,lat1 DOUBLE,lng2 DOUBLE,lat2 DOUBLE) ...

  3. 温(Xue)习排序算法

    最近忙着找工作,虽然排序算法用得到的情况不多,但不熟悉的话心里始终还是感觉没底. 于是今天给温习了其中的四个排序算法(与其说是温习,不如说是学习...因为感觉自己好像从来木有掌握过它们...) 一.选 ...

  4. 解题报告 - 577. Employee Bonus

    Select all employee's name and bonus whose bonus is < 1000. Table:Employee +-------+--------+---- ...

  5. 如果使用mybatis的逆向工程生成的po类及mapper,如果我们想要进行的对数据库的操作在mapper中没有对应的接口函数:比如生成的mapper接口中没有按照姓名及性别混合条件查询。我们的解决办法是:使用逆向工程生成的对应表的Example文件。

    1.使用mybatis逆向工程生成的po类中包含UserExample文件(我的数据库表名为User). 2. 创建UserExample对象,然后对加入条件.对应的测试代码为: /* * 通过姓名和 ...

  6. Eclipse下初用lucene

    lucene是apache的一个开源项目,一个开放源代码的全文检索引擎工具包. 1. 首先下载lucene,下载地址来自<lucene实战>第2版(页面加载比较忙,等~) http://w ...

  7. mongodb-win32-i386-3.0.6 使用常见错误

    1.Error parsing YAML config file: yaml-cpp: error at line 3, column 28: unknown escape character: m ...

  8. 大数据技术之_19_Spark学习_05_Spark GraphX 应用解析 + Spark GraphX 概述、解析 + 计算模式 + Pregel API + 图算法参考代码 + PageRank 实例

    第1章 Spark GraphX 概述1.1 什么是 Spark GraphX1.2 弹性分布式属性图1.3 运行图计算程序第2章 Spark GraphX 解析2.1 存储模式2.1.1 图存储模式 ...

  9. 装饰者模式及C++实现

    装饰者模式 时常会遇到这样一种情况,我已经设计好了一个接口,并且也有几个实现类,但是这时我发现我设计的时候疏忽了,忘记了一些功能,或者后来需求变动要求加入一些功能,最简单的做法就是修改接口,添加函数, ...

  10. eFrameWork学习笔记-eOleDB

    eOleDB是eFrameWork框架下基础的数据访问类,用于执行SQL语句,返回DataTable,分页,返回数据库所有库,库的所有表,表的所有列,Json导入.导出等. HTML: <div ...