线程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.youkuaiyun.com/le119126/article/details/45025297。 http://blog.youkuaiyun.com/caigen1988/article/details/7901248、http://blog.youkuaiyun.com/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);
#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_join
bool 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);
void runInThread();
bool started_;
pthread_t pthreadId_;
pid_t tid_;
ThreadFunc func_;
string name_;
static AtomicInt32 numCreated_;
};
}
#endif
//CurrentThread.h
namespace muduo
{
namespace CurrentThread
{
//internal
extern __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 调用 detail
int 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++教程网