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(); } 测试结果如下,可以看到,两个版本的运行效果是一模一样的!
