CurrentThread主要就是为了获得当前线程的id和获取调用堆栈,Thread封装了创建线程及相关的api
CurrentThread.h中声明的一些函数在Thread.cpp中定义,两者关系紧密,所以可以放在一起看
CurrentThread.h 源码注释
#ifndef MUDUO_BASE_CURRENTTHREAD_H
#define MUDUO_BASE_CURRENTTHREAD_H
#include "Types.h"
namespace muduo{
//线程局部变量
namespace CurrentThread{
/*
extern关键字:用在变量声明中常有这样一个作用,在*.cpp文件中声明一个全局变量,这个全局的变量如果要被引用,
就放在*.h中并用extern来声明。
*/
//__thread修饰的变量在每个线程中有独立的实体,各个线程中的变量互不干扰。
//__thread只能修饰POD类型变量(与C兼容的原始数据)。
extern __thread int t_cachedTid;//缓存中的线程id
extern __thread char t_tidString[32];//线程id的字符串形式
extern __thread int t_tidStringLength;//线程id字符串的长度
extern __thread const char* t_threadName;//线程名陈
void cacheTid();//在thread.cpp 中定义
inline int tid(){//本线程第一次调用的时候才进行系统调用,以后都是直接从thread local缓存的线程id拿到结果
/*
这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大.
这是一种性能优化,在这里就表示t_cachedTid为正的概率大,即直接返回t_cachedTid的概率大
*/
if(__builtin_expect(t_cachedTid==0,0)){
cacheTid();
}
return t_cachedTid;
}
inline const char* tidString(){// for logging
return t_tidString;
}
inline int tidStringLength(){// for logging
return t_tidStringLength;
}
inline const char* name(){
return t_threadName;
}
bool isMainThread();//在thread.cpp 中定义
void sleepUsec(int64_t usec); // for testing
string stackTrace(bool demangle);
}
}
#endif
CurrentThread.cpp 源码注释
#include"CurrentThread.h"
#include <cxxabi.h>
#include <execinfo.h>
#include <stdlib.h>
#include <type_traits>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>
namespace muduo{
//线程局部变量
namespace CurrentThread{
//__thread修饰的变量在每个线程中有独立的实体,各个线程中的变量互不干扰。
//__thread只能修饰POD类型变量(与C兼容的原始数据)。
__thread int t_cachedTid = 0;
__thread char t_tidString[32];
__thread int t_tidStringLength = 6;
__thread const char* t_threadName = "unknown";
/*
C++11中引入了static_assert这个关键字,用来做编译期间的断言,因此叫作静态断言
static_assert(常量表达式,"提示字符串")
如果第一个参数常量表达式的值为false,会产生一条编译错误。
错误位置就是该static_assert语句所在行,第二个参数就是错误提示字符串。
性能方面:由于static_assert是编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失
*/
/*
template< class T, class U >
struct is_same;
如果T与U具有同一const-volatile限定的相同类型,则is_same<T,U>::value为true,否则为false。
要求pid_t必须是int类型
*/
static_assert(std::is_same<int, pid_t>::value, "pid_t should be int");
/*void cacheTid(){
if (t_cachedTid == 0)
{
t_cachedTid=static_cast<pid_t>(::syscall(SYS_gettid));
}
}*/
/*
该函数是用来获取调用堆栈,debug用
首先,关于调用堆栈:假设我们有几个函数,fun1,fun2,fun3,fun4,且fun1调用fun2,fun2调用fun3,fun3调用fun4.
在fun4运行过程中,我能可以从线程当前堆栈中了解到调用它的几个函数分别是谁。
从函数的顺序来看,fun4,fun3,fun2,fun1呈现出一种“堆栈”的特征,最后被调用的函数出现在最上方,
因此称这种关系为调用堆栈(call stack)
使用场景:
当故障发生时,如果程序被中断,我们基本只可以看到最后出错的函数。
利用call stack,我们可以知道当出错函数被谁调用的时候出错。这样一层层看上去,可以猜测出错误的原因
*/
string stackTrace(bool demangle){
string stack;
const int max_frames = 200;
void* frame[max_frames];
/*
int backtrace (void **buffer, int size);
该函数用来获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针数组。
参数size用来指定buffer中可以保存多少个void* 元素。
函数返回值是实际获取的指针个数,最大不超过size大小在buffer中的指针实际是从堆栈中获取的返回地址,
每一个堆栈框架有一个返回地址。
注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容。
*/
int nptrs = ::backtrace(frame, max_frames);
/*
char **backtrace_symbols (void *const *buffer, int size);
该函数将从backtrace函数获取的信息转化为一个字符串数组。
参数buffer是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值),
函数返回值是一个指向字符串数组的指针,它的大小同buffer相同。
每个字符串包含了一个相对于buffer中对应元素的可打印信息。
它包括函数名,函数的偏移地址和实际的返回地址。
backtrace_symbols生成的字符串都是malloc出来的,但是不要最后一个一个的free,
因为backtrace_symbols会根据backtrace给出的callstack层数,一次性的将malloc出来一块内存释放,
所以,只需要在最后free返回指针就OK了。
*/
char** strings = ::backtrace_symbols(frame, nptrs);
if (strings)
{
size_t len = 256;
char* demangled = demangle ? static_cast<char*>(::malloc(len)) : nullptr;
for (int i = 1; i < nptrs; ++i) // skipping the 0-th, which is this function
{
if (demangle)
{
// https://panthema.net/2008/0901-stacktrace-demangled/
// bin/exception_test(_ZN3Bar4testEv+0x79) [0x401909]
char* left_par = nullptr;
char* plus = nullptr;
for (char* p = strings[i]; *p; ++p)
{
if (*p == '(')
left_par = p;
else if (*p == '+')
plus = p;
}
if (left_par && plus)
{
*plus = '\0';
int status = 0;
char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status);
*plus = '+';
if (status == 0)
{
demangled = ret; // ret could be realloc()
stack.append(strings[i], left_par+1);
stack.append(demangled);
stack.append(plus);
stack.push_back('\n');
continue;
}
}
}
// Fallback to mangled names
stack.append(strings[i]);
stack.push_back('\n');
}
free(demangled);
free(strings);
}
return stack;
}
}//CurrentThread
}//muduo
Thread.h 源码注释
#ifndef MUDUO_BASE_THREAD_H
#define MUDUO_BASE_THREAD_H
#include "Atomic.h"
#include "CountDownLatch.h"
#include "Types.h"
#include <functional>
#include <memory>
#include <pthread.h>
namespace muduo
{
class Thread: noncopyable{
public:
//定义回调函数
typedef std::function<void()> ThreadFunc;
explicit Thread(ThreadFunc,const string& name=string());
~Thread();
//开启线程的接口
void start();
//等待线程结束
int join();
bool started() const {return started_;}
//返回线程id
pid_t tid() const { return tid_; }
//返回线程名
const string& name() const { return name_; }
//已经启动的线程个数
static int numCreated() { return numCreated_.get(); }
private:
void setDefaultName();
bool started_;//启动标识,表示线程是否启动
bool joined_;
/*
pthreadId_是线程ID,但是这个值不是唯一的,在不同进程下的两个线程可能会有同一个线程ID,
当出现进程p1中的线程t1要与进程p2中的线程t2通信的情况时,需要一个真实的线程id唯一标识,即tid。
glibc没有实现gettid的函数,可以通过linux下的系统调用syscall(SYS_gettid)来获得
*/
pthread_t pthreadId_;
//该线程的真实id,可以唯一标识一个线程
pid_t tid_;
//真正调用的回调函数
ThreadFunc func_;
//线程名称
string name_;
CountDownLatch latch_;
//统计当前线程数
static AtomicInt32 numCreated_;
};
}//muduo
#endifThread.cpp 源码注释
#include "Thread.h"
#include "CurrentThread.h"
#include "Exception.h"
#include "Logging.h"
#include <type_traits>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>
namespace muduo
{
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();
/*
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
pthread_atfork()在fork()之前调用,当调用fork时,内部创建子进程前在父进程中会调用prepare,
内部创建子进程成功后,父进程会调用parent ,子进程会调用child。
*/
pthread_atfork(NULL, NULL, &afterFork);
}
};
ThreadNameInitializer init;
//线程创建的函数的参数类型就是这个结构体,另外,这个结构体中的runInThread()是真正的线程函数,被线程函数startThread() 调用
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()
{
*tid_ = muduo::CurrentThread::tid();
tid_ = NULL;
latch_->countDown();
latch_ = NULL;
muduo::CurrentThread::t_threadName = name_.empty() ? "muduoThread" : name_.c_str();
::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName);
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
}
}
};
void* startThread(void* obj)
{
ThreadData* data = static_cast<ThreadData*>(obj);
data->runInThread();
delete data;
return NULL;
}
}//detail
void CurrentThread::cacheTid()
{
if (t_cachedTid == 0)
{
t_cachedTid = detail::gettid();
t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
}
}
bool CurrentThread::isMainThread()
{
return tid() == ::getpid();
}
void CurrentThread::sleepUsec(int64_t usec)
{
struct timespec ts = { 0, 0 };
ts.tv_sec = static_cast<time_t>(usec / Timestamp::kMicroSecondsPerSecond);
ts.tv_nsec = static_cast<long>(usec % Timestamp::kMicroSecondsPerSecond * 1000);
::nanosleep(&ts, NULL);
}
AtomicInt32 Thread::numCreated_;
/*
线程池中调用Thread构造函数如下:
//将this分配给runInThread,相当于构造Thread(this.runInThread,name+id)并加入线程数组。线程函数是runInThread
threads_.push_back(new muduo::Thread(
boost::bind(&ThreadPool::runInThread, this), name_+id));
threads_[i].start(); //启动每个线程,但是由于线程运行的函数是runInThread,所以会阻塞
线程函数即为:this.runInThread,线程名为name+id。 (注意此处的runInThread不是Thread类中的,而是ThreadPool中的)
*/
Thread::Thread(ThreadFunc func, const string& n)
: started_(false),
joined_(false),
pthreadId_(0),
tid_(0),
func_(std::move(func)),
name_(n),
latch_(1)
{
setDefaultName();
}
Thread::~Thread()
{
if (started_ && !joined_)
{
pthread_detach(pthreadId_);
}
}
void Thread::setDefaultName()
{
int num = numCreated_.incrementAndGet();
//如果线程创建参数中没有线程名,即name_为空,则将name_赋值为 "Thread+id" id就是创建的第几个线程
if (name_.empty())
{
char buf[32];
snprintf(buf, sizeof buf, "Thread%d", num);
name_ = buf;
}
}
/*
线程启动函数,调用pthread_create创建线程,线程函数为detail::startThread,
传递给线程函数的参数data是在heap上分配的,data存放了线程真正要执行的函数记为func、线程id、线程name等信息。
detail::startThread会调用func启动线程,所以detail::startThread可以看成是一个跳板或中介。
*/
void Thread::start()//线程启动函数,调用pthread_create创建线程
{
assert(!started_);//确保线程没有启动
started_ = true;//设置标记,线程已经启动
// FIXME: move(func_)
detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);
if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
{
started_ = false;//创建线程失败,设置标记线程未启动
delete data; // or no delete?
LOG_SYSFATAL << "Failed in pthread_create";
}
else
{
latch_.wait();
assert(tid_ > 0);
}
}
int Thread::join()
{
assert(started_);
assert(!joined_);
joined_ = true;
return pthread_join(pthreadId_, NULL);
}
}//muduo
测试程序
关于测试程序中的boost::bind():
可以支持函数对象、函数、函数指针、成员函数指针,并且绑定任意参数到某个指定值上或者将输入参数传入任意位置。bind(f, 1, 2)等价于f(1, 2); bind(g, 1, 2, 3)等价于g(1, 2, 3);
一般情况下,bind 与 function 配合使用。
#include "Thread.h"
#include "CurrentThread.h"
#include <string>
#include <boost/bind.hpp>
#include <stdio.h>
#include <unistd.h>
void mysleep(int seconds)
{
timespec t = { seconds, 0 };
nanosleep(&t, NULL);
}
void threadFunc()
{
printf("tid=%d\n", muduo::CurrentThread::tid());
}
void threadFunc2(int x)
{
printf("tid=%d, x=%d\n", muduo::CurrentThread::tid(), x);
}
void threadFunc3()
{
printf("tid=%d\n", muduo::CurrentThread::tid());
mysleep(1);
}
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();
printf("t1.tid=%d\n", t1.tid());
t1.join();
//bind绑定普通函数
muduo::Thread t2(boost::bind(threadFunc2, 42),
"thread for free function with argument");
t2.start();
printf("t2.tid=%d\n", t2.tid());
t2.join();
//bind绑定成员函数
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());
}测试结果
knopfler@DESKTOP-3UDOCBE:~/muduo/base$ make g++ -o test test.cpp Mutex.h Condition.h Condition.cpp CurrentThread.h CurrentThread.cpp CountDownLatch.h CountDownLatch.cpp Atomic.h Thread.h Thread.cpp Timestamp.h Timestamp.cpp Logging.h Logging.cpp TimeZone.h TimeZone.cpp LogStream.h LogStream.cpp Date.h Date.cpp -lpthread knopfler@DESKTOP-3UDOCBE:~/muduo/base$ ./test pid=7171, tid=7171 t1.tid=7172 tid=7172 tid=7173, x=42 t2.tid=7173 tid=7174, Foo::x_=87.530000 tid=7175, Foo::x_=87.530000, text=Shuo Chen number of created threads 4
版权声明:本文为qq_39898877原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。