基于Linux pthread库模拟实现的线程类
基于Linux pthread库模拟实现的线程类
- 线程类的本质
- 对线程生命周期的控制
- 线程创建
- 线程属性获取
- 线程等待
- `int(void*)` 线程v1.0参考
- `void(T)`线程v1.1参考
线程类的本质
无论什么语言的线程类,只要它还在当前计算机中运行,就必须调用计算机中原有的原生线程库内的接口对线程进行控制。所以所谓的封装使用的还是原生线程,但会根据实际需求例如安全、效率等对创建的线程进行控制。
这里模仿 C++11 的thread类对线程类进行描述:
- 希望一个线程有自己的线程ID。
- 希望线程有自己的线程名。
- 用户可能希望查看当前线程的运行状态。
- 线程拥有属于自己的线程函数。
- 线程可由用户决定何时启动。
- 线程运行结束后不建议可选返回自己的返回值。因为函数有
void和其他返回值类型,这使得功能实现上变得很复杂。
其他可选:
- 一般情况下不希望线程自己进行隐式构造一个线程。
- 线程支持移动构造,但不能支持拷贝构造。
对线程生命周期的控制
线程创建
这里我们希望线程类的对象能根据用户的需求,选择是否创建。
所以构造函数只会对线程的基本信息赋值,对象真正创建线程还需要调用额外的函数,这里可设置接口start。若线程启动成功则设置状态runstate为真。
线程属性获取
这里的线程类的属性有 3 种:
- 线程名。
- 线程运行状态。
- 线程 ID 。
分别给不同的属性设置接口即可。
线程等待
线程和进程一样,若线程已经退出,则默认返回时会被等待。
所以可通过设置等待用的接口join,通过pthread_join使得主线程等待子线程。
若线程状态变量runstate为假,则说明等待成功,join返回真。
否则join所在的线程会进行等待,修改等待结果并返回。
int(void*)线程v1.0参考
mythread.hpp:
#pragmaonce#include<functional>#include<pthread.h>#include<string>usingstd::string;// 想让线程的主体函数和main函数一样的运行模式namespacemystd{usingfunc_t=std::function<int(void*)>;template<classT>classMyThread{public:MyThread(conststring&name,func_t fc,T data=T()):_name(name),_func(fc),_data(data),_tid(pthread_t()),_runstate(false){}~MyThread(){}staticvoid*ThreadRoutine(void*args){// 通过类内方法,启动进程// 因为原生线程库无法上传包装器MyThread*ts=static_cast<MyThread*>(args);ts->_func(args);returnnullptr;}boolstart(){if(pthread_create(&_tid,nullptr,ThreadRoutine,this)==0){_runstate=true;returntrue;}returnfalse;}booljoin(){// 线程正常退出if(!_runstate)returntrue;// 主动等待线程// 可以获取返回值,现在是模型,所以不考虑设为nullptrif(pthread_join(_tid,nullptr)==0){_runstate=false;returntrue;}// 等待失败returnfalse;}// getter接口boolrunstate()const{return_runstate;}stringname()const{return_name;}pthread_ttid()const{return_tid;}private:pthread_t _tid;// 线程IDstring _name;// 线程名bool_runstate;// 线程运行状态func_t _func;// 线程本体函数T _data;// 线程类初始数据};}// namespace mystd测试参考:
#include"mythread.hpp"// 为使用mt19937#include<chrono>#include<iostream>#include<random>#include<unistd.h>#include<vector>usingmystd::MyThread;usingstd::cout;usingstd::endl;usingstd::to_string;usingstd::vector;intf(void*args){sleep(1);MyThread<int>*mtd=static_cast<MyThread<int>*>(args);cout<<mtd->name()<<", 启动"<<endl;// 精度更高的随机数类std::mt19937_64rng(std::chrono::steady_clock().now().time_since_epoch().count());for(inti=1,x=0;i<=5;i++){x=rng()%10000+1;cout<<x<<endl;sleep(1);}return0;}voidf1(){MyThread<int>myThread("thread1",f);if(myThread.start()){cout<<"主线程:thread1运行成功, 运行状态:"<<myThread.runstate()<<endl;}else{cout<<"主线程:thread1运行失败\n";}if(myThread.join()){cout<<"主线程:等待thread1成功, 运行状态:"<<myThread.runstate()<<endl;}else{cout<<"主线程:等待thread1失败\n";}}voidf2(){vector<MyThread<int>>ts;for(inti=1;i<=5;i++){string name="Thread"+to_string(i);ts.push_back(MyThread<int>(name,f));}for(auto&x:ts){if(!x.start()){cout<<x.name()<<"启动异常\n";exit(-1);}sleep(1);}for(auto&x:ts){if(!x.join()){cout<<x.name()<<"回收异常\n";exit(-1);}cout<<x.name()<<"成功回收\n";}}intmain(){f2();return0;}void(T)线程v1.1参考
mythread.hpp
#pragmaonce#include<functional>#include<pthread.h>#include<string>namespacemystd{usingstd::function;usingstd::string;// 因为线程函数的返回值从始至终都没有使用// 所以直接上传返回值为void的函数template<classT>usingfunc_t=function<void(T)>;// 创造一个支持选择参数类型的线程类模板template<classT>classMyThread{private:pthread_t _tid;// 线程IDstring _name;// 线程名bool_runstate;// 线程运行状态func_t<T>_func;// 线程本体函数T _data;// 计划上传给线程本体函数的参数public:explicitMyThread(conststring&name,func_t<T>fc,T data):_tid(pthread_t()),_name(name),_runstate(false),_func(fc),_data(data){}~MyThread(){}staticvoid*ThreadRoutine(void*args){// args一般情况下为线程对象MyThread*t=static_cast<MyThread*>(args);t->_func(t->_data);returnnullptr;}boolstart(){if(pthread_create(&_tid,nullptr,ThreadRoutine,this)==0){return_runstate=true;}returnfalse;}booljoin(){if(!_runstate)returntrue;if(pthread_join(_tid,nullptr)==0){_runstate=false;returntrue;}returnfalse;}// getter接口boolrunstate()const{return_runstate;}stringname()const{return_name;}pthread_ttid()const{return_tid;}};}// namespace mystd测试程序:
#include"mythread.hpp"#include<iostream>#include<string>#include<vector>usingnamespacestd;usingnamespacemystd;structThreadData{vector<int>_arr;int*_sum;ThreadData(vector<int>arr,int*sum):_arr(arr),_sum(sum){}};voidadd(void*args){vector<int>&arr=static_cast<ThreadData*>(args)->_arr;int*sum=static_cast<ThreadData*>(args)->_sum;for(auto&x:arr)(*sum)+=x;}intmain(){vector<int>a={1,1,4,5,1,4,0,9,0,9,8,1,0};intsum=0;ThreadDatadt(a,&sum);string ThreadName="t1";MyThread<ThreadData*>t1(ThreadName,add,&dt);if(t1.start()){if(!t1.join())return-1;}cout<<sum<<endl;return0;}但若编译器支持 C++11 ,还是建议使用thread。