- 线程标识符
Linux中,每个进程有一个pid, 类型为pid_t, 由getpid()获得;
Linux下POSIX线程也有id,类型为pthread_t, 由pthread_self()获得,其id由线程库维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。POSIX线程库的实现也是一个进程;
有时需要从一个进程p1向另外一个进程的线程p2发送信号时,既不能使用p2的pid,更不能使用p2的pthread id, 而只能使用该线程的真实pid,称为tid。由gettid() 可得到tid,但glibc未实现该函数,只能通过Linux系统调用syscall(SYS _gettid)获取。
//CurrentThread.h
// __thread修饰的变量是线程局部存储的
extern __thread int t_cachedTid; //线程真实pid(tid)的缓存
extern __thread char t_tidString[32];//tid的字符串表示形式
//thread.cc
pid_t gettid()
{
return static_cast<pid_t>(::syscall(SYS_gettid));
}
void CurrentThread::cacheTid()
{
if (t_cachedTid == 0)
{
t_cachedTid = detail::gettid();
t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
}
}
- 缓存tid,以免每次都要getid(),浪费系统资源
- __thread只能修饰POD(plain old data)类型,即与C兼容的原始数据
//Thread.cc
//用结构体封装了线程的属性和方法,供Thread调用
struct ThreadData
{
typedef muduo::Thread::ThreadFunc ThreadFunc;
ThreadFunc func_;
string name_;
pid_t* tid_;
CountDownLatch* latch_;
ThreadData(ThreadFunc func, const string& name, pid_t* tid,
CountDownLatch* latch)
: func_(std::move(func)),
name_(name),
tid_(tid),
latch_(latch)
{ }
void runInThread(){
...
}
}
void Thread::start()//创建线程
{
...
if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
...
}
void* startThread(void* obj)//线程函数
{
ThreadData* data = static_cast<ThreadData*>(obj);
data->runInThread();
delete data;
return NULL;
}
//如果线程tid等于当前进程id,则为主线程
bool CurrentThread::isMainThread()
{
return tid() == ::getpid();
}
//Thread.cc
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);
}
};
- *int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (child)(void)) 调用fork时,创建子进程前在父进程中会调用prepare,创建子进程成功后,父进程调用parent,子进程调用child。
- fork可能在主线程或子线程中调用,fork()得到一个新进程,新进程只有一个执行序列,只有一个线程(调用fork的线程被继承下来)。
- 实际上,对于编写多线程,最好不调用fork().