__thread
线程标识符
(1)Linux中,每个进程有一个pid,类型pid_t,由getpid()取得。Linux下的POSIX线程也有一个id,类型 pthread_t,由pthread_self()取得,该id由线程库维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。Linux中的POSIX线程库实现的线程其实也是一个进程(LWP),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。
(2)有时候我们可能需要知道线程的真实pid。比如进程P1要向另外一个进程P2中的某个线程发送信号时,既不能使用P2的pid,更不能使用线程的pthreadid,而只能使用该线程的真实pid,称为tid。
(3)有一个函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。
Thread类
muduo库采用基于对象的方式实现线程类:
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_; }//线程是否启动
pid_t tid() const { return tid_; }//获取当前线程的tid
const string& name() const { return name_; }
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
#ifndef MUDUO_BASE_CURRENTTHREAD_H
#define MUDUO_BASE_CURRENTTHREAD_H
namespace muduo
{
namespace CurrentThread
{
extern __thread int t_cachedTid;//extern表示该变量是一个已经定义的外部变量(定义在Thread.cc中)
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() // for logging
{
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
{
namespace CurrentThread
{
// __thread修饰的变量是线程局部存储的全局变量,每个线程都有一份
__thread int t_cachedTid = 0; // 设置t_cachedTid来缓存线程真实的pid(tid),
// 为了减少::syscall(SYS_gettid)系统调用的次数
// 提高获取tid的效率
__thread char t_tidString[32]; // 这是tid的字符串表示形式
__thread const char* t_threadName = "unknown";
const bool sameType = boost::is_same<int, pid_t>::value;
BOOST_STATIC_ASSERT(sameType);
}// namespace CurrentThread
namespace detail
{
pid_t gettid()
{
return static_cast<pid_t>(::syscall(SYS_gettid));
}
void afterFork()
{
muduo::CurrentThread::t_cachedTid = 0;
muduo::CurrentThread::t_threadName = "main";
CurrentThread::tid();
// no need to call pthread_atfork(NULL, NULL, &afterFork);
}
class ThreadNameInitializer
{
public:
ThreadNameInitializer()
{
muduo::CurrentThread::t_threadName = "main";
CurrentThread::tid();
pthread_atfork(NULL, NULL, &afterFork);
}
};
ThreadNameInitializer init;//全局变量
}// namespace detail
}// namespace muduo
using namespace muduo;
void CurrentThread::cacheTid()
{
if (t_cachedTid == 0)
{
t_cachedTid = detail::gettid();
int n = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
assert(n == 6); (void) n;
}
}
bool CurrentThread::isMainThread()
{
return tid() == ::getpid();//如果当前线程的tid和当前进程的pid相等,说明当前线程是主线程
}
AtomicInt32 Thread::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();//首先获取当前线程的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
}
}
测试程序Thread_test.cc
#include <muduo/base/Thread.h>
#include <muduo/base/CurrentThread.h>
#include <string>
#include <boost/bind.hpp>
#include <stdio.h>
void threadFunc()
{
printf("tid=%d\n", muduo::CurrentThread::tid());
}
void threadFunc2(int x)
{
printf("tid=%d, x=%d\n", muduo::CurrentThread::tid(), x);
}
class Foo
{
public:
explicit Foo(double x)
: x_(x)
{
}
void memberFunc()
{
printf("tid=%d, Foo::x_=%f\n", muduo::CurrentThread::tid(), x_);
}
void memberFunc2(const std::string& text)
{
printf("tid=%d, Foo::x_=%f, text=%s\n", muduo::CurrentThread::tid(), x_, text.c_str());
}
private:
double x_;
};
int main()
{
printf("pid=%d, tid=%d\n", ::getpid(), muduo::CurrentThread::tid());
muduo::Thread t1(threadFunc);
t1.start();
t1.join();
muduo::Thread t2(boost::bind(threadFunc2, 42),
"thread for free function with argument");
t2.start();
t2.join();
Foo foo(87.53);
muduo::Thread t3(boost::bind(&Foo::memberFunc, &foo),
"thread for member function without argument");
t3.start();
t3.join();
muduo::Thread t4(boost::bind(&Foo::memberFunc2, boost::ref(foo), std::string("Shuo Chen")));
t4.start();
t4.join();
printf("number of created threads %d\n", muduo::Thread::numCreated());
}
pthread_atfork
在源码的muduo::detail命名空间下,用到了pthread_atfork,如下图所示:
可以看出,init是一个全局变量,在创建时调用其构造函数,在构造函数中调用pthread_atfork。下面分析这样写的原因:
在Linux中,在线程中调用fork的时候只复制当前线程到子进程,也就是说除了调用fork的线程外,其他线程在子进程中“蒸发”了。
fork可能是在主线程中调用,也可能在子线程中调用。fork在主线程中调用时,新进程仍然会复制该主线程,而新的线程在新的进程中仍然是主线程,此时调用afterFork没变化;fork在子线程中调用时,新进程仍然会复制该子线程,而新的线程在新的进程中此时变成了主线程,因此需要修改其线程id和名称,这就是调用全局函数afterFork的目的。