muduo源码分析:线程类Thread封装

来源:互联网 发布:淘宝钻石号在哪里买 编辑:程序博客网 时间:2024/05/17 01:20

线程id

Linux中,每个进程有一个pid,类型pid_t,由getpid()取得。

Linux下的POSIX线程也有一个id,类型 pthread_t,由pthread_self()取得,该id由线程库维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。Linux中的POSIX线程库实现的线程其实也是一个进程(LWP),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。

有时候我们可能需要知道线程的真实pid。比如进程P1要向另外一个进程P2中的某个线程发送信号时,既不能使用P2的pid,更不能使用线程的pthread id,而只能使用该线程的真实pid,称为tid
有一个函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。

return syscall(SYS_gettid)

线程私有数据

1.__thread : gcc内置的线程局部存储设施

__thread只能修饰POD类型
POD类型(plain old data),与C兼容的原始数据,例如,结构和整型等C语言中的类型是 POD 类型,但带有用户定义的构造函数或虚函数的类则不是
__thread string t_obj1(“cppcourse”); // 错误,不能调用对象的构造函数
__thread string* t_obj2 = new string; // 错误,初始化只能是编译期常量
__thread string* t_obj3 = NULL; // 正确

2.Posix线程私有数据

(Thread-specific Date)TSD
实现原理:http://blog.csdn.net/le119126/article/details/45025297。 http://blog.csdn.net/caigen1988/article/details/7901248、http://blog.csdn.net/hudashi/article/details/7709430
相当于二级索引,key数组(一级索引)整个进程共享,标志哪些key使用与否,每个线程有自己pkey数组(二级索引),存在pthread结构中,pkey数组存储自己私有数据的指针。key --》pkey- -》私有数据指针

pthread_key_create(创建一个键),pthread_setspecific(为一个键设置线程私有数据),pthread_getspecific(从一个键读取线程私有数据),pthread_key_delete(删除一个键)。这几个函数的声明如下:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key,void (*destr_function)(void *));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);

boost :: is_same

类型是否一样

存储类:

存储类可分为auto、register、static、extern、mutable、thread_local(__thread)等

mutable

mutable存储类只能用于类的数据成员,不能用于普通变量。具有mutable性质的类的数据成员打破了类对象的const限定,允许修改类的mutable的数据成员,即便类的其它成员仍然是const只读属性。

thread_local

适用于命名空间内的变量、全局作用域的变量、函数内部定义的静态变量,如果使用了thread_local关键字,则在运行时不同的线程具有该变量的不同的存储位置,即各有各的副本。因此,具有thread_local存储类的变量,必然具有static存储类性质,不管是否使用了static关键字
class CurrenThread{    public: static __thread  int a;};   __thread int test::a=0;

源码分析

detail命名空间:一些公共、初始化操作,其中有调用CurrentThread::tid( )
CurrentThread::tid() 在CurrentThread 命名空间,也有调用detail的detail::gettid( )
他们有相互调用
CurrentThread命名空间:本线程基本信息,没有封装成类(类用__thread麻烦),直接裸露在命名空间里,都是__thread 私有数据

ThreadNameInitalizer init;//全局变量,在main函数开始前就构造,完成启动初始化配置(pthread_atfork),且就一次,这一次就标示了 主线程的名字CurrenThread::t_ThreadName="main"  

//Thread.h#ifndef MUDUO_BASE_THREAD_H#define MUDUO_BASE_THREAD_H#include <muduo/base/Atomic.h>#include <muduo/base/Types.h>#include <boost/function.hpp>#include <boost/noncopyable.hpp>#include <pthread.h>namespace muduo{class Thread : boost::noncopyable{public:typedef boost::function<void ()> ThreadFunc;explicit Thread(const ThreadFunc&,const string& name=string());~Thread();void start();int join();// return pthread_joinbool started() const {return started_;}pthread_t pthreadId() const {return pthreadId_;}pid_t tid() const {return tid_;}static int numCreated() {return numCreated_.get();}private:static void*startThread(void* thread);voidrunInThread();boolstarted_;pthread_tpthreadId_;pid_ttid_;ThreadFuncfunc_;stringname_;static AtomicInt32 numCreated_;};}#endif
//CurrentThread.hnamespace muduo{namespace CurrentThread{//internalextern__thread int t_cachedTid;extern __thread char t_tidString[32];extern __thread const char* t_threadName;void cacheTid();inline int tid(){if(t_cachedTid==0)  cacheTid();return t_cachedTid;}inline const char* tidString(){return t_tidString;}inline const char* name(){return t_threadName;}bool isMainThread();}}#endif
//Thread.cc#include <muduo/base/Thread.h>#include <muduo/base/CurrentThread.h>#include <muduo/base/Exception.h>//#include <muduo/base/Logging.h>#include <boost/static_assert.hpp>#include <boost/type_traits/is_same.hpp>#include <errno.h>#include <stdio.h>#include <unistd.h>#include <sys/syscall.h>#include <sys/types.h>#include <linux/unistd.h>namespace muduo{/*class CurrenThread{public: static __thread  int a;}; __thread int test::a=0;*///CurrentThread 线程信息,如果组织成类就得像上面那样麻烦,所以直接裸露在命名空间里,全是私有全局变量namespace CurrentThread{__thread int t_cachedTid = 0;//真实pid__thread char t_tidString[32];__thread const char* t_threadName = "unknown";//其实只有main,started,finished,crashed这四个名字( 在Thread::runInThread()中对其修改 ),用来表示线程状态的而已,线程真正名字时Thread::name_const bool sameType = boost::is_same<int, pid_t>::value;BOOST_STATIC_ASSERT(sameType);}//一些公共初始化操作namespace detail{pid_t gettid(){return static_cast<pid_t>(::syscall(SYS_gettid));}//设置fork后的主线程状态,不需要显示调用,下面用全局变量ThreadNameInitalizer的构造函数调用了void afterFork(){muduo::CurrentThread::t_cachedTid=0;muduo::CurrentThread::t_threadName="main";CurrentThread::tid();//如果t_cachedTid等于0,就先调用cachedTid(),在返回t_cachedTide.其实这句就是为了缓存t_cachedTid( detail 中调用 CurrentThread)}class ThreadNameInitalizer{public:ThreadNameInitalizer(){muduo::CurrentThread::t_threadName = "main";CurrentThread::tid();pthread_atfork(NULL,NULL,&afterFork);}};ThreadNameInitalizer init;//全局变量,在main函数开始前就初始化,且就一次,这一次就标示了main 主线程}using namespace muduo;void CurrentThread::cacheTid(){if(t_cachedTid == 0){t_cachedTid = detail::gettid();//CurrentThread 调用 detailint n = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);  assert(n == 6); (void) n;   }}bool CurrentThread::isMainThread(){return tid() == ::getpid();}AtomicInt32 Thread::numCreated_;//因为numCreated_是静态的,一定要定义,不能只在头文件中声明Thread::Thread(const ThreadFunc& func, const string& n):started_(false), pthreadId_(0), tid_(0), func_(func), name_(n){numCreated_.increment();}Thread::~Thread(){}void Thread::start(){assert(!started_);started_=true;errno = pthread_create(&pthreadId_, NULL, &startThread, this);if(errno != 0){//LOG_SYSFATAL << "Failed in pthread_create";}}int Thread::join(){assert(started_);return pthread_join(pthreadId_,NULL);}void* Thread::startThread(void* obj){Thread* thread=static_cast<Thread*>(obj);thread->runInThread();return NULL;}void Thread::runInThread(){tid_=CurrentThread::tid();muduo::CurrentThread::t_threadName=name_.c_str();try{func_();muduo::CurrentThread::t_threadName="finished";}catch(const Exception& ex){muduo::CurrentThread::t_threadName="crashed";fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());fprintf(stderr, "reason: %s\n", ex.what());fprintf(stderr, "stack trace: %s\n", ex.stackTrace());abort();  }  catch (const std::exception& ex)  {muduo::CurrentThread::t_threadName = "crashed";fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());fprintf(stderr, "reason: %s\n", ex.what());abort();  }  catch (...)  {muduo::CurrentThread::t_threadName = "crashed";fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str());throw; // rethrow  }}}
相关:线程属性与线程局部存储

参考:c++教程网

           muduo网络库

           linux多线程服务器端编程》.陈硕




0 0
原创粉丝点击