我们的TcpServer服务器程序可能有很多的EventLoop,每个EventLoop都有很多Channel,某个Channel上的事件要通过所属的Eventloop线程去处理,Eventloop在这里涉及到获取当前线程的ID
top -Hp pid
:查看某个进程的线程信息,-H
显示线程信息,-p
指定pid
muduo CurrentThread.h
部分源码
#ifndef MUDUO_BASE_CURRENTTHREAD_H
#define MUDUO_BASE_CURRENTTHREAD_H
#include "muduo/base/Types.h"
namespace muduo
{
namespace CurrentThread
{
// internal
extern __thread int t_cachedTid;
extern __thread char t_tidString[32];
extern __thread int t_tidStringLength;
extern __thread const char* t_threadName;
void cacheTid();
inline int tid() // 用于获取当前线程tid
{
if (__builtin_expect(t_cachedTid == 0, 0)) // 如果t_cachedTid == 0,则获取tid存储到t_cachedTid
{
cacheTid();
}
return t_cachedTid;
}
我们看到都是全局变量,存放在.data
段,即所有线程共享可以共享这些变量
这些全局变量还用__thread
修饰,实际上C++11提供的thread_local
,意思是这虽然是全局变量,但是会在每一个线程栈存储一份拷贝,这个线程对这个变量的更改,别的线程是看不到的,可以理解为线程内全局(thread_local),通常会在多线程环境中使用
我们来看一下Thread.cc
中的cacheTid()
void CurrentThread::cacheTid()
{
if (t_cachedTid == 0)
{
t_cachedTid = detail::gettid();
t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
}
}
Thread.cc
中的gettid()
pid_t gettid()
{
return static_cast<pid_t>(::syscall(SYS_gettid));
}
因为tid的访问syscall(SYS_gettid)
是一个系统调用,涉及用户空间和内核空间的切换,为了提高效率,第一次执行syscall(SYS_gettid)时就把当前线程的tid存储起来,后边如果再需要tid就从变量t_cachedTid获取
重写CurrentThread.h
#pragma once
#include <unistd.h>
#include <sys/syscall.h>
namespace CurrentThread{
extern __thread int t_cachedTid;
void cacheTid();
// 内联只在当前文件起作用
inline int tid(){
if(__builtin_expect(t_cachedTid == 0, 0)){
// 如果t_cachedTid == 0,表示还没有获取tid,需要通过系统调用syscall获取
cacheTid();
}
return t_cachedTid;
}
}
重写CurrentThread.cc
#include "CurrentThread.h"
namespace CurrentThread{
__thread int t_cachedTid = 0;
void cacheTid(){
if(t_cachedTid == 0){
// 通过Linux系统调用,获取当前线程tid
t_cachedTid = static_cast<pid_t>(syscall(SYS_gettid));
}
}
}