您的位置:首页 > 编程语言 > C语言/C++

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

2017-03-11 07:38 465 查看

在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();
}

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 线程