线程池(Linux实现)
讨论QQ群:135202158
本文技术参考了sourceforge项目c thread pool,链接:http://sourceforge.net/projects/cthpool/
线程池如上一篇随笔(http://www.cnblogs.com/zzqcn/p/3585003.html)提到的内存池一样,也是一种池化策略,在启动时(或者更高级的,运行时按一定策略分配)预先开启N个线程,当没有工作要做时,这些线程处于睡眠中;一旦有工作加入工作队列,其中的某些线程就会醒来,处理这些工作,完成后继续睡眠 。
要实现线程池(只针对本文的简单实现而言),应设计和构建3样东西:
- 含N个线程的线程组
- 工作队列
- 工作线程例程
线程组和工作队列表示如下:
/*
* 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实现)的更多相关文章
- 线程池 ------ linux C实现
大多数的网络服务器,包括Web服务器都具有一个特点,就是单位时间内必须处理数目巨大的连接请求,但是处理时间却是比较短的.在传统的多线程服务器模型中是这样实现的:一旦有个请求到达,就创建一个新的线程,由 ...
- 一个简单的linux线程池(转-wangchenxicool)
线程池:简单地说,线程池 就是预先创建好一批线程,方便.快速地处理收到的业务.比起传统的到来一个任务,即时创建一个线程来处理,节省了线程的创建和回收的开销,响应更快,效率更高. 在linux中,使用的 ...
- Linux平台下线程池的原理及实现
转自:http://blog.csdn.net/lmh12506/article/details/7753952 前段时间在github上开了个库,准备实现自己的线程池的,因为换工作的事,一直也没有实 ...
- 高效线程池之无锁化实现(Linux C)
from:http://blog.csdn.net/xhjcehust/article/details/45844901 笔者之前练手写过一个小的线程池版本(已上传至https://github.co ...
- Linux + C + Epoll实现高并发服务器(线程池 + 数据库连接池)(转)
转自:http://blog.csdn.net/wuyuxing24/article/details/48758927 一, 背景 先说下我要实现的功能,server端一直在linux平台下面跑,当客 ...
- 在Linux下写一个线程池以及线程池的一些用法和注意点
-->线程池介绍(大部分来自网络) 在这个部分,详细的介绍一下线程池的作用以及它的技术背景以及他提供的一些服务等.大部分内容来自我日常生活中在网络中学习到的一些概念性的东西. -->代码 ...
- Linux C 一个简单的线程池程序设计
最近在学习linux下的编程,刚开始接触感觉有点复杂,今天把线程里比较重要的线程池程序重新理解梳理一下. 实现功能:创建一个线程池,该线程池包含若干个线程,以及一个任务队列,当有新的任务出现时,如果任 ...
- Linux多线程实践(9) --简单线程池的设计与实现
线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...
- Linux下简易线程池
线程池简介 线程池是可以用来在后台执行多个任务的线程集合. 这使主线程可以自由地异步执行其他任务.线程池通常用于服务器应用程序. 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会 ...
- Linux C 实现一个简单的线程池
线程池的定义 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如 ...
随机推荐
- Redis 字典的实现
[Redis 字典的实现] 注意 dict 类型使用了两个指针,分别指向两个哈希表. 其中, 0 号哈希表(ht[0])是字典主要使用的哈希表, 而 1 号哈希表(ht[1])则只有在程序对 0 号哈 ...
- react-navigation 3.x版本的使用
安装配置请看: react-navigation 3.x版本的安装以及react-native-gesture-handler配置 2.0以前版本: StackNavigator - 一次只渲染一个页 ...
- Cook-Torrence Illumination Model 的一些数学说明
Cook-Torrence 光照模型如下: 这个Io就是计算后最终的光强,主要是用来计算镜面反射光,漫反射和环境光的计算和Phong模型一致. F:Fresnel反射系数.主要用来说明反射光强度占入射 ...
- ESXi系统命令行下启动虚拟机
从命令行启动虚拟机: 用命令列出虚拟机的ID:vim-cmd vmsvc/getallvms |grep <vm name>注意: 第一列输出是vmid. 用命令查看虚拟机启动状态:vim ...
- Groovy使用List集合
获取List集合中的元素 def lst = [1,3,4,1,8,9,2,6] println lst[-1] println lst[-2] 输出结果: 输出: 6 2 使用Range(范围)对象 ...
- [SoapUI]怎样配置SoapUI运行的不同环境,并在Jenkins上面通过命令调用不用的环境
配置SoapUI运行的不同环境 Groovy 脚本来控制environment 在Jenkins上面通过命令调用不用的环境 http://www.soapui.org/Test-Automation/ ...
- jvm编译环境搭建 Debina篇
这里参考了 <Java虚拟机精讲> <深入理解Java虚拟机 JVM高级特性与最佳实践> http://www.cnblogs.com/zxfdream/p/5411511.h ...
- iOS Orientation获取
[iOS Orientation获取] 1.[[UIDevice sharedInstance] orientation] 必须调用beginGeneratingDeviceOrientationNo ...
- Text Relatives II
[Text Relatives II] When your app determines that the user has requested the edit menu—which could b ...
- java.sql.SQLException: Access denied for user ''@'localhost' (using password: YES)
这个问题是说明你的JDBC数据库连接位置出错了,请仔细检查 private static String url; private static String username; private sta ...