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

    xiaoxiao2021-03-25  97

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

    转载请注明原文地址: https://ju.6miu.com/read-26223.html

    最新回复(0)