C++的线程库
C++当中有一个创建多线程的函数:
但是这里要注意:任何语言在Linux中要实现多线程,必定要使用pthread库。
C++11中的多线程,本质就是对pthread库的封装。
线程的分离
默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
这种就是线程分离。
这是获得当前调用这个函数id的接口:
#include <iostream> #include <pthread.h> #include <cassert> #include <string> #include <vector> #include <unistd.h> #include <cstdlib> using namespace std; string changid(const pthread_t &pthread_id) { char buffer[128]; snprintf(buffer,sizeof(buffer),"0x%x",pthread_id); return buffer; } void* start_routine(void* args) { string name = static_cast<const char*>(args); while(true) { cout << name << "running" << changid(pthread_self()) << endl; sleep(1); } } int main() { pthread_t tid; pthread_create(&tid, nullptr, start_routine, (void*)"thread"); cout << "main thread running... new thread id:" << changid(tid) <<endl; pthread_join(tid, nullptr); return 0; }
然后来看线程分离的接口:
参数是线程id。
#include <iostream> #include <pthread.h> #include <cassert> #include <string> #include <cstring> #include <vector> #include <unistd.h> #include <cstdlib> using namespace std; string changid(const pthread_t &pthread_id) { char buffer[128]; snprintf(buffer,sizeof(buffer),"0x%x",pthread_id); return buffer; } void* start_routine(void* args) { string name = static_cast<const char*>(args); pthread_detach(pthread_self());//设置自己为分离状态 int count = 5; while(count) { cout << name << "running" << changid(pthread_self()) << endl; count--; sleep(1); } return nullptr; } int main() { pthread_t tid; pthread_create(&tid, nullptr, start_routine, (void*)"thread"); string main_tid = changid(pthread_self()); cout << "main thread running... new thread id:" << changid(tid) << "main thread id:" << main_tid << endl; sleep(2);//这里是为了防止新线程还没有进行分离主线程就已经开始等待了 //如果线程设置了分离,这里就不能等待了 int n = pthread_join(tid, nullptr); cout << "result" << n << ":" << strerror(n) << endl;//这里进行报错之后继续向下运行代码 return 0; }
如何理解每个线程都有自己独立的栈结构
首先了解一下线程库和轻量级进程的关系:
我们用户都是通过pthread库来创建线程的。
在原生线程库当中,我们用这些接口创建的线程别人也可以同时使用。(因为是共享库)
并且也需要对这些线程进行管理:
每个结构体对应一个轻量级的进程。
Linux的方案;用户级线程,这些属性在库中,内核提供线程执行流的调度。
Linux用户级线程:Linux内核轻量级进程 == 1:1
那么线程的id究竟是什么呢?
也就是说一旦线程结束,通过返回值就会传给共享区的TCB中。
这也能说明为什么每个线程都有自己的栈结构了。
主线程使用的栈是在主线程栈,其他线程的栈是在共享区。(其实也就是线程库当中)
那么什么是线程的局部存储呢?
之前创建过一个全局变量,证明两个线程都会共享这个变量。
如果在这个变量前面加上:__thread就可以将一个内置类型设置为线程局部存储。
也就是说给每个线程都来一份这个变量,两个线程在使用这个变量的时候互不影响。
如果以后给线程设置私有属性可以加上这个。
封装线程接口
这里就用Linux的线程接口来实现C++中的多线程部分功能。
#include <iostream> #include <pthread.h> #include <cassert> #include <string> #include <cstring> #include <vector> #include <unistd.h> #include <cstdlib> #include <memory> using namespace std; class Thread;//声明 class Context//上下文,相当于一个大号的结构体 { public: Thread *this_; void* args_; public: Context():this_(nullptr),args_(nullptr) {} ~Context() {} }; class Thread { typedef function<void* (void*)> func_t; public: //这里需要加一个静态,因为不加静态就是类成员函数,还有一个隐藏的this指针,也就说明这等于前面有一个缺省参数 //所以在类内创建线程,想让对应的线程执行方法需要在方法前面加一个static static void* start_routine(void* args) { //但是静态方法不能调用成员方法或者成员变量,这里可以设置一个上下文 Context* ctx = static_cast<Context*>(args); void* ret = ctx->this_->run(ctx->args_);//这里让自身去调用这个方法 delete ctx; return ret; } void* run(void* args) { return _func(args);//调用该函数 } Thread(func_t func,void* args,int num):_func(func),_args(args) { char buffer[1024]; snprintf(buffer, sizeof(buffer), "thread_%d", num); _name = buffer; Context* ctx = new Context(); ctx->this_ = this; ctx->args_ = _args;//这里是将自身的部分数据传给ctx int n = pthread_create(&_tid, nullptr, start_routine, ctx);//这里要通过调用函数来转化,直接传func是不行的,因为类型是C++的类,不是C语言的类 assert(n==0); (void)n; } void join() { int n = pthread_join(_tid,nullptr); assert(n==0); (void)n; } ~Thread() {} private: string _name;//线程名字 pthread_t _tid;//线程id func_t _func;//线程调用的函数 void* _args;//传给函数的参数 };
#include "Thread.hpp" void* thread_run(void* args) { string work_type = static_cast<const char*>(args); while(true) { cout << "我是一个新线程:" << work_type <<endl; sleep(1); } } int main() { unique_ptr<Thread> thread1(new Thread(thread_run, (void*)"hellothread",1)); unique_ptr<Thread> thread2(new Thread(thread_run, (void*)"countthread",2)); unique_ptr<Thread> thread3(new Thread(thread_run, (void*)"logthread",3)); thread1->join(); thread2->join(); thread3->join(); return 0; }