在window环境下,实现类似std::thread的线程类

在window环境下,实现类似std::thread的线程类

zerglurker

      C++11中的std::thread是一个非常强大的线程类,但是这个线程类有一个很尴尬的地方,就是这个线程类创建的线程,其实用户是不可控的。

      比如:我要创建一个初始为暂停状态的线程。这个类就无法实现。再比如:我要在线程启动后,需要暂停,强制终结这个线程,也难以办到。又比如:我要在线程执行完成后,重新执行一次,也同样难以办到!std::thread类必须重新声明对象!

      这样这个玩意就难以用于实现线程池了,否则必须要频繁的启动和销毁线程,或者自己设计一个复杂的工作类!

      基于上面的问题,我只能自己设计一个线程类,来实现和std::thread类似的线程,但是又没有上述的问题。

      既然std::thread已经很不错了,我需要的是参考std::thread的实现,来实现自己的线程类即可。

      下面和各位分享一下我的实现过程:

            std::thread的核心只有两个部分构成:

            _Launch和bind,_Launch用于启动线程,bind用于构造线程函数,并适配各种输入参数和函数类型。

            另外std::thread用到了两个技巧:一是将构造函数声明为一个模板函数,以方便用户使用——这个技巧非常有意思,可以用于提升类的可复用性和自动适配能力;二是使用了可变参数模板——这个技巧使得其传入的线程函数没有固定的参数数量限制。

            可是看到这里坑爹的事情来了:std::thread将线程管理全部外包到_Launch,这怎么搞!我还指望它自己有什么线程管理呢!完全没有看到如何启动用户传入的函数啊!

            没有办法,只好继续看_Launch函数——STL模板库真心难看,这代码和命名写的,真心虐心,真心不想看啊发火

            _Launch函数构造了一个_LaunchPad本地对象,使用_Launch来启动线程,还是没有看到如何启动用户传入的函数!——好吧,到这里然并卵,只好捏着鼻子继续看。

            _LaunchPad实现了一个_Run函数,在_Run函数里面终于看到我想要的东西了可怜

_Target _Local(_STD forward<_Target>(_Ln->_MyTarget));
_Ln->_Release();
_Local();//←←←就是这货!!!
            这里补充一个知识点就是std::forward的含义。其实也没有什么特别的含义,就是将参数转为一个右值引用,类似于复制。

            区别在于forward不需要对象具有构造函数,可以直接使用对象的原值。防止在参数传递过程中,触发一些复制构造函数或者其他构造函数。

            回到std::thread的构造函数,这里的_Local其实是一个std::bind类型的数据

            看到这里,我终于明白该怎么做了,我只需要保存这个bind类型的数据,在我自己的线程函数里面去调用它的函数调用操作即可(即调用operator())

            可是动手之后,又发现一个坑,怎么声明这个数据类型呢?

            bind本身是一个模板,直接声明,我的线程类也会变成一个模板类,那尼玛用起来就酸爽了(有毅力的童鞋可以去尝试一下)

            想了半天,突然想到一个办法:要构造一个自己的数据类型,基类提供一个operator()接口,方便子类去实现

            通过多态调用到子类的operator()接口,来完成函数调用!!

            到这里基本大的坑都填完了,不多废话了,下面直接上代码

            可变数据类型

class Data{
public:
	Data(){ pData = NULL; }
	~Data(){}
	virtual void operator()() = 0;
	void* pData;
};

template<class DataType>
class CMyData:public Data{
public:
	CMyData() :
		Data()
	{
	}
	~CMyData(){
		if (pData){
			delete (DataType*)pData;
			pData = NULL;
		}
	}
	explicit CMyData(const DataType& d){
		pData = (void*)new DataType(d);
	}
	virtual void operator()(){
		if (pData != NULL){
			DataType* pLocale = (DataType*)pData;
			(*pLocale)();
		}
	}
};

            自定义线程类——不要介意名字和代码格式,这些都是细枝末节哈 大笑

class Test{
public:
	template<class _Fn,
	class... _Args>
		explicit Test(_Fn&& _Fx, _Args&&... _Ax){
		Init(
			std::bind(
			std::_Decay_copy(_STD forward<_Fn>(_Fx)),
			std::_Decay_copy(_STD forward<_Args>(_Ax))...
			)
		);
	}
	template<class bindType>
	void Init(bindType&& bindData){
		_Data = new CMyData<bindType>(std::forward<bindType>(bindData));
		if (_Data != NULL){
			m_hThread = (HANDLE)_beginthreadex(NULL, 0, &Test::ThreadEntry, this, CREATE_SUSPENDED, &m_nThreadID);
		}
	}
	virtual int Start(){
		if ((m_hThread == INVALID_HANDLE_VALUE) || (m_hThread == NULL)){
			return -1;
		}
		//if ((m_nStatus == TH_INIT) || (m_nStatus == TH_PAUSE))
		{
			if (ResumeThread(m_hThread) != 0){
				return -2;
			}
			//m_nStatus = TH_RUNNING;
		}
		return 0;
	}
private:
	Data* _Data;
	void ThreadFunc(){
		if (_Data != NULL){
			(*_Data)();
		}
	}
	static unsigned __stdcall  ThreadEntry(void* arg){
		Test* pThread = (Test*)arg;
		if (pThread){
			pThread->ThreadFunc();
		}
		_endthreadex(0);
		return 0;
	}
	//线程句柄
	HANDLE m_hThread;
	//线程ID
	unsigned m_nThreadID;
};
            测试代码

void test001(int argc, char**argv, char**env)
{
	//CThreadImplement < func>;
	std::thread th(func, argc, argv, env);
	th.detach();
	Test tt(func, argc, argv, env);
	tt.Start();
	getchar();
}

            测试结果如下,可以看到,两个版本的运行效果是一模一样的!





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值