模板类多线程(可直接使用)

接触多线程已经有较长一段时间了,在工作中也经常用到多线程线程池等,于是打算写一个通用的模板类,方便以后的调用。当开始写的时候,我觉得这应该比较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>
  


                
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值