参考自:b站视频
https://www.bilibili.com/video/BV1Zt411K7Gg?spm_id_from=333.999.0.0
timestamp时间戳
类图:
封装了unix时钟值,且可以调用类方法转换成xx年xx月xx日的形式,用于日志记录时的时间标识。
以下是一些实现细节:
class Timestamp : public muduo::copyable,
public boost::equality_comparable<Timestamp>,
public boost::less_than_comparable<Timestamp>
{
...
}
copyable是一个用于标识当前类可以拷贝的类,继承它就意味着当前类可以拷贝。也就是实现了值语义(也就是可以拷贝,拷贝之后的值与原来的值没有关系;与之相反的是对象语义,要么不能拷贝,要么拷贝之后与原对象仍有关系,比如指针的拷贝)。
equality_comparable和less_than_comparable是两个模板类,继承前者可以自动实现类型比较函数(重载==号),继承后者的话,只要自己重载<号,就可以自动重载<、<=、>=。
Atomic原子操作
原子操作是指这个操作在执行过程中不会被其他打断。原子操作的粒度比锁更细,适用于某个变量的读写,c++11后,标准库也支持原子类型。
volatile关键字:
用volatile修饰变量的时候,系统总会从变量所在的内存读取数据,而不会读取它在寄存器中的吧备份。
volatile本质上是避免某些指令在编译过程中被优化掉。可以做到两件事:1、阻止编译器为了提高效率把一个变量缓存到寄存器而不写回内存。2、阻止编译器调整volatile变量的指令顺序。
Execption异常
主要实现了stackTrace,也就是保存了函数栈,发生异常的时候可以输出栈帧的情况,以便于了解哪个函数调用出现了异常。
内部调用了backtarce和backtrace_symbols函数保存了栈信息。
thread线程类
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_; }
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_;
pthread_t pthreadId_;
pid_t tid_;
ThreadFunc func_;
string name_;
CountDownLatch latch_;
static AtomicInt32 numCreated_;
};
具体实现有些复杂,先来说说一些基本的数据成员。
started_:线程启动标记
joined_:调用了pthread_join,主线程需要等待子线程回收
pthreadId_:线程id
tid_:线程所属的进程id,muduo库采用多线程模型,不太明白保存进程id有什么用处
func_:线程入口函数
latch_:倒计时门闩,可用于线程池初始化时的同步
thread类的关键函数是start,线程启动的方式并不是直接将func_作为pthread_create的参数进行线程创建,而使用了更委婉的方法。
在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()
{...}
};
在start函数启动时,会先创建一个ThreadData对象,该对象拥有Thread对象的数据成员信息,并依赖于它来创建线程。
void Thread::start()
{
assert(!started_);
started_ = true;
detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);
if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
{
started_ = false;
delete data;
LOG_SYSFATAL << "Failed in pthread_create";
}
else
{
latch_.wait();
assert(tid_ > 0);
}
}
这里的startThread是一个外部的函数,以ThreadData对象作为参数,该函数最终会调用ThreadData对象的runInThread成员函数,这时才会会真正地调用线程入口函数func_。
线程的信息会保存在CurrentThread命名空间里,该命名空间的变量都用__thread修饰。该关键字表示该变量是线程独享的。