讨论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. $in 操作符

    [$in 操作符] The $in operator selects the documents where the value of a field equals any value in the ...

  2. PLSQL result set exceeds the maximum size(100M)if necessary,you can explicitly confinue this query

    在PL SQL 里执行一条语句,当反正信息达到2w条时,弹出如下内容:result set exceeds the maximum size(100M)if necessary,you can exp ...

  3. ios实现分发下载

    背景:原来公司Jenkins打包后的ipa和apk都是通过第三方的平台托管,手动上传,然后去扫二维码下载.虽然第三方平台有Jenkins插件来直接上传到该平台,但是想自己进行管理.所以就自己来做安装包 ...

  4. Java常用的输出调试技巧

    --------siwuxie095                 Eclipse 开发中常用的输出调试技巧:     先在左侧的 Package Explorer,右键->New->J ...

  5. NOSQL之Redis、MongDB、Habase、Cassandra的介绍与比较

    一.Redis介绍     1.1Redis优点 (1)Redis拥有非常丰富的数据结构: (2)Redis提供事务的功能,可以保证一串命令的原子性,中间不会被任何打断. (3)数据存储在内存中,读写 ...

  6. py学习之FTP

    1.FTP之参数解析与命令分发 a) 层级目录如下 b) 配置文件如下 #!/usr/bin/env python # -*- coding:utf8 -*- import socket sk=soc ...

  7. ESXi系统命令行下启动虚拟机

    从命令行启动虚拟机: 用命令列出虚拟机的ID:vim-cmd vmsvc/getallvms |grep <vm name>注意: 第一列输出是vmid. 用命令查看虚拟机启动状态:vim ...

  8. Android应用程序的基本组件介绍

    1.Activity和View Activity是Android应用中负责与用户交互的组件,它只能通过setContentView(View)来显示指定组件. View组件是所有UI控件.容器空间的基 ...

  9. String 、 StringBuffer 和 StringBuilder

    StringBuffer (一个线程安全的可变字符串序列,用于多线程) A thread-safe, mutable sequence of characters. StringBuilder (可变 ...

  10. FIREDAC(DELPHI10 or 10.1)提交数据给ORACLE数据库的一个不是BUG的BUG

    发现FIREDAC(DELPHI10 or 10.1)提交数据给ORACLE数据库的一个不是BUG的BUG,提交的表名大小写是敏感的. 只要有一个表名字母的大小写不匹配,ORACLE就会认为是一个不认 ...