接触多线程已经有较长一段时间了,在工作中也经常用到多线程线程池等,于是打算写一个通用的模板类,方便以后的调用。当开始写的时候,我觉得这应该比较easy,能够很快的实现。而在写的过程中才发现不是那么容易。
这篇文档有模板的相关内容:http://blog.csdn.net/lqk1985/article/details/3136364
1、首先考虑到的是通用性,怎样让这个多线程通用呢?可能会因为业务的不同效果不一样,思来想去,决定采用resquest->process(多线程处理)->res模型吧。而不是将处理结果直接放在request(直接放在request应该可以减少多次new res实例吧)
2、对于任务的传入,结果的传出,是否采用阻塞模式呢?后来决定通过函数重载两种模式都支持。
3、对于多任务,多输出,很自然的采用了std::queue,构造一个线程安全的队列予以处理。
4、多线程怎样根据不同的业务进行不同的处理,重载任务基类的实现函数,由业务自己决定处理方式。只是将这个任务的执行这交给线程池来实现。
感觉所有的问题都解决了,就开始动工了,前前后后大概花了1天的时间,写成了一个通用的模板,个人感觉适用性还是挺强的,直接任务的传入,就可以取结果了,由线程池予以任务处理。方便又高效。
5、居然忘了说:该模板类只支持类指针模式,不支持类模式,后面正确两种模式都支持吧。
好了废话少说直接贴代码(在linux平台测试并通过):
1、模板线程池cthreadpool.h
<span style="font-size:18px;">#ifndef __CTHREADPOOL_H
#define __CTHREADPOOL_H
/*
*threadpool::初始化时,只能用类指针
*
*push_work::添加任务
*
*get_workres::获取处理结果
*/
#include <iostream>
#include <queue>
#include <pthread.h>
#include "ctaskqueue.h"
template <class DTYPEIN, class DTYPEOUT>
class cthreadpool
{
public:
cthreadpool(){}
~cthreadpool(){}
static cthreadpool<DTYPEIN, DTYPEOUT>* getinstance();
static bool create_threadpool(int num);
static void destroy_threadpool();
virtual void process();
public:
static void* run(void* arg);
bool create_thread();
void push_work(DTYPEIN request);
void task_deal();
DTYPEOUT get_workres(); //阻塞
DTYPEOUT get_workres(int type); //非阻塞
private:
static cthreadpool<DTYPEIN, DTYPEOUT>* m_threadpool;
ctaskqueue<DTYPEIN> m_quein;<span style="white-space:pre"> </span>//两个队列:一个任务的输入,一个输出<span style="white-space:pre"> </span>
ctaskqueue<DTYPEOUT> m_queout;
};
template <class DTYPEIN, class DTYPEOUT>
cthreadpool< DTYPEIN, DTYPEOUT> * cthreadpool<DTYPEIN, DTYPEOUT>::m_threadpool=NULL;</span>
<span style="font-size:18px;">template <class DTYPEIN, class DTYPEOUT>
cthreadpool<DTYPEIN, DTYPEOUT>* cthreadpool< DTYPEIN, DTYPEOUT>::getinstance()
{
return m_threadpool;
}
template <class DTYPEIN, class DTYPEOUT>
bool cthreadpool< DTYPEIN, DTYPEOUT>::create_threadpool(int num)
{
m_threadpool = new cthreadpool<DTYPEIN, DTYPEOUT>;
for (int i=0; i<num; i++)
{
if (!m_threadpool->create_thread())
return false;
}
return true;</span>
<span style="font-size:18px;">}
template <class DTYPEIN, class DTYPEOUT>
void cthreadpool< DTYPEIN, DTYPEOUT>::destroy_threadpool()
{
delete m_threadpool;
}
template <class DTYPEIN, class DTYPEOUT>
void cthreadpool< DTYPEIN, DTYPEOUT>::process()
{
task_deal();
}
template <class DTYPEIN, class DTYPEOUT>
void* cthreadpool< DTYPEIN, DTYPEOUT>::run(void* arg)
{
cthreadpool<DTYPEIN, DTYPEOUT>* pthis = (cthreadpool<DTYPEIN, DTYPEOUT>*) arg;
pthis->process();
}
template <class DTYPEIN, class DTYPEOUT>
bool cthreadpool< DTYPEIN, DTYPEOUT>::create_thread()
{
pthread_t id;
if (pthread_create(&id, NULL, run, this) == 0)
return true;
return false;
}
template <class DTYPEIN, class DTYPEOUT>
void cthreadpool< DTYPEIN, DTYPEOUT>::push_work(DTYPEIN request)
{
m_quein.push_element(request);
}
template <class DTYPEIN, class DTYPEOUT>
void cthreadpool< DTYPEIN, DTYPEOUT>::task_deal()
{
while (1)
{
DTYPEIN taskin = NULL;
taskin = m_quein.get_element();
if (taskin != NULL)
{
DTYPEOUT taskout = dynamic_cast<DTYPEOUT>(taskin->task_process());
m_queout.push_element(taskout);
delete taskin;
}
}
}
template <class DTYPEIN, class DTYPEOUT>
DTYPEOUT cthreadpool< DTYPEIN, DTYPEOUT>::get_workres()
{
return m_queout.get_element();
}
template <class DTYPEIN, class DTYPEOUT>
DTYPEOUT cthreadpool< DTYPEIN, DTYPEOUT>::get_workres(int type)</span>
<span style="font-size:18px;">{
return m_queout.get_element(type);
}
#endif
</span>
2、线程池任务队列 ctaskqueue.h
<span style="font-size:18px;"><span style="font-size:12px;">#ifndef __CTASKQUEUE_H
#define __CTASKQUEUE_H
#include <queue>
#include <pthread.h>
#include <iostream>
#include "csemlock.hpp"
#include "cscalock.hpp"
#include "cmutex.hpp"
#include <semaphore.h>
template <class KIND>
class ctaskqueue
{
public:
ctaskqueue()
{
sem_init(&_sem_cond, 0, 1);
}
~ctaskqueue()
{
sem_destroy(&_sem_cond);
}
void push_element(KIND com);
KIND get_element();
KIND get_element(int type);
private:
std::queue<KIND> m_queue;
cmutex _mutex_lock;<span style="white-space:pre"> </span>//互斥锁,简单封装了下,代码就不贴了
//csemlock _cond_lock; 最初的条件信号量,各种死锁
cscalock _sca_lock;<span style="white-space:pre"> </span>//最开始采用了SCA无锁线程模式,后来替换成了常规模式。
sem_t _sem_cond;
};
template <class KIND>
void ctaskqueue<KIND>::push_element(KIND com)
{
_mutex_lock.lock_mutex();
if (m_queue.empty())
{</span></span>
<span style="font-size:18px;"><span style="font-size:12px;"> m_queue.push(com);
sem_post(&_sem_cond);
std::cout<<"thrd-id:"<<pthread_self()<<" send a broadcast."<<std::endl;
}
else
{ m_queue.push(com);
}
_mutex_lock.unlock_mutex();
}
template <class KIND>
KIND ctaskqueue<KIND>::get_element()
{
KIND taskin = NULL;
while(true)
{
_mutex_lock.lock_mutex();
if (m_queue.empty())
{ _mutex_lock.unlock_mutex();
std::cout<<"thrd-id:"<<pthread_self()<<" will block."<<std::endl;
sem_wait(&_sem_cond);
continue;
}
KIND val = NULL;
if (!m_queue.empty())
{
val = m_queue.front();
m_queue.pop();
}
_mutex_lock.unlock_mutex();
return val;
}
}
template <class KIND>
KIND ctaskqueue<KIND>::get_element(int type)
{
KIND val = NULL;
_mutex_lock.lock_mutex();
if (!m_queue.empty())
{ </span>
<span style="font-family: 微软雅黑;"><span style="font-size:18px;"><span style="white-space:pre"> </span> val = m_queue.front();</span></span>
<span style="font-size:18px;"> m_queue.pop();
}
_mutex_lock.unlock_mutex();
return val;
}
#endif </span>
好了以上就是线程池的实现代码了,现在要做的就是测试了。
3、线程池的任务基类(任务类必须继承该类,并实现处理函数)
<span style="font-size:18px;"><span style="font-size:12px;">class cthreadtask
{
public:
virtual cthreadtask* task_process()=0;
}; </span>
<span style="font-size:18px;"> 4、构建任务基类</span></span>
<span style="font-size:18px;"> 在该例子的测试中:任务描述:将requst请求中的char数组进行倒序并生成res </span>
<span style="font-size:18px;"> testclassB为res, testclass为request</span>
<span style="font-size:18px;">class testclassB:public cthreadtask
{
public:
testclassB(char* pdata)
{
memcpy(m_data, pdata, strlen(pdata)+1);
}
~testclassB(){}
void get_res()
{
std::cout<<"thrd-id:"<<m_thrdid<<" deal-res:"<<m_data<<std::endl;
}
void set_thrdid(pthread_t id)
{ <span style="font-family: 微软雅黑;">m_thrdid = id;</span><span style="font-family: 微软雅黑;">}</span></span>
<span style="font-family: 微软雅黑;"><span style="font-size:18px;"> </span></span>
<span style="font-size:18px;">public:
cthreadtask* task_process(){}
private:
char m_data[100];
pthread_t m_thrdid;
};
class testclass:public cthreadtask
{
public:
testclass(char* pdata)
{
memcpy(m_data, pdata, strlen(pdata)+1);
}
~testclass(){}
public:
cthreadtask* task_process();
private:
char m_data[100];
};
cthreadtask* testclass::task_process()
{</span>
<span style="font-size:18px;"> int ncur = strlen(m_data);
int nlen = ncur / 2;
ncur = ncur - 1;
char tmp;
for (int i=0; i<nlen; i++)
{
tmp = m_data[i];
m_data[i] = m_data[ncur];
m_data[ncur] = tmp;
ncur--;
}
testclassB* dealres = new testclassB(m_data);
dealres->set_thrdid(pthread_self());
return dealres;
}</span>
5、构建main函数,让程序run起来
<span style="font-size:18px;"> 主线程不停的放任务,线程池进行处理,再单独创建了一个线程获取处理结果。OK,该线程池就完美的跑起来了,运行稳定,无内存泄露等。
int dealnum = 0;
int putnum = 0;
void signal_deal(int no)
{ </span>
<span style="font-size:18px;"> std::cout<<"put-num:"<<putnum<<std::endl;
std::cout<<"deal-num:"<<dealnum<<std::endl;
exit(0);
}
static void* run(void* arg)
{
testclassB* res;
while (1)
{
res = cthreadpool<testclass*, testclassB*>::getinstance()->get_workres(0);
if (res != NULL)
{
res->get_res();
dealnum++;
res->get_res();
delete res;
}
else
{
std::cout<<"get res = NULL"<<std::endl;
}
std::cout<<"deal-num:"<<dealnum<<std::endl;
usleep(1000);
}
}
int main(void)
{
//处理线程 request
if (cthreadpool<testclass*, testclassB*>::create_threadpool(5) == false)
{
std::cout<<"thread pool create error."<<std::endl;
cthreadpool<testclass*, testclassB*>::destroy_threadpool();
exit(0);
}
signal(SIGINT, signal_deal );
//获取处理结果线程
pthread_t id;
pthread_create(&id, NULL, run, NULL);
int num = 0;
usleep(100*1000);
while (1)
{
testclass* task1 = new testclass ("1234567890");
testclass* task2 = new testclass ("abcdefghij");
testclass* task3 = new testclass ("111102222");
testclass* task4 = new testclass ("22222222~33333333");
cthreadpool<testclass*, testclassB*>::getinstance()->push_work(task1);
cthreadpool<testclass*, testclassB*>::getinstance()->push_work(task2);
cthreadpool<testclass*, testclassB*>::getinstance()->push_work(task3);
cthreadpool<testclass*, testclassB*>::getinstance()->push_work(task4);
putnum += 4;
std::cout<<"the num:"<<putnum<<std::endl;
usleep(10*1000);
}
return 0;
}
<span style="font-family: 微软雅黑;">6、总结</span></span></span>
<span style="font-size:18px;"> 在写的过程中,我最初将模板的申明和实现分开了,然后再编译的时候,各种链接错误,当时各种无解,后来问了师兄终于解决了。然后在构造安全对列的时候,由于要判断队列为空线程阻塞等情况我采用了条件信号量的方式,然后线程内部各种死锁,当我不停的释放条件信号量时才运行正常,可这不是正道,于是各种排查,然后各种无解,头都打了,一怒之下换成了信号量方式,然后终于运行正常了,可具体错误在哪暂时还不知道,如果大家有兴趣,可以将死锁代码贴出来,求大家指导。 <span style="font-family: 微软雅黑;">在linux平台上测试通过,并正常运行,任务正确的处理,无内存泄露,死锁等现象。 </span></span>
<span style="font-size:18px;"> <span style="white-space:pre"> </span>写blog的是否发:我的代码是用soure insight编辑的,为方便阅读,我习惯性的空行,然后在CSDN上就有很多空行。 </span>