Muduo源码Base
Thread类
class Thread : noncopyable
{
public:
typedef std::function<void ()> ThreadFunc;
explicit Thread(ThreadFunc, const string& name = string());
// FIXME: make it movable in C++11
~Thread();
void start();
int join(); // return pthread_join()
bool started() const { return started_; }
// pthread_t pthreadId() const { return pthreadId_; }
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_;
};
在看Thread类之前先看CountDownLatch类
这是一个倒计时 类似于linux下提供的屏障
一个简单的例子 主线程等待十个副线程完成操作再运行
#include<iostream>
#include<thread>
#include<vector>
#include"CountDownLatch.h"
using namespace std;
using namespace mynamespace;
CountDownLatch latch(10);
void threadfunc(int i){
printf("my thread id is %d\n",
i);
latch.CountDown();
}
int main(){
vector<thread> vec;
for(int i=0;i<10;++i){
vec.emplace_back(thread(threadfunc,i));
}
latch.Wait();
printf("main thread is running\n");
for(auto &it:vec)
{
it.join();
}
}
Atomic类
主要是以下三个函数
__sync_val_compare_and_swap
__sync_fetch_and_add
__sync_lock_test_and_set
```cpp
type __sync_fetch_and_add(type *ptr, type value, ...); // m+n
type __sync_fetch_and_sub(type *ptr, type value, ...); // m-n
type __sync_fetch_and_or(type *ptr, type value, ...); // m|n
type __sync_fetch_and_and(type *ptr, type value, ...); // m&n
type __sync_fetch_and_xor(type *ptr, type value, ...); // m^n
type __sync_fetch_and_nand(type *ptr, type value, ...); // (~m)&n
/* 对应的伪代码 */
{ tmp = *ptr; *ptr op= value; return tmp; }
{ tmp = *ptr; *ptr = (~tmp) & value; return tmp; } // nand
type __sync_add_and_fetch(type *ptr, type value, ...); // m+n
type __sync_sub_and_fetch(type *ptr, type value, ...); // m-n
type __sync_or_and_fetch(type *ptr, type value, ...); // m|n
type __sync_and_and_fetch(type *ptr, type value, ...); // m&n
type __sync_xor_and_fetch(type *ptr, type value, ...); // m^n
type __sync_nand_and_fetch(type *ptr, type value, ...); // (~m)&n
/* 对应的伪代码 */
{ *ptr op= value; return *ptr; }
{ *ptr = (~*ptr) & value; return *ptr; } // nand
bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...);
type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...);
/* 对应的伪代码 */
{ if (*ptr == oldval) { *ptr = newval; return true; } else { return false; } }
{ if (*ptr == oldval) { *ptr = newval; } return oldval; }
GNU bool
https://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html
Thread类
构造函数以及初始化
Thread::Thread(ThreadFunc func, const string& n)
: started_(false),
joined_(false),
pthreadId_(0),
tid_(0),
func_(std::move(func)),
name_(n),
latch_(1)
{
setDefaultName();
}
void Thread::setDefaultName()
{
int num = numCreated_.incrementAndGet();
if (name_.empty())
{
char buf[32];
snprintf(buf, sizeof buf, "Thread%d", num);
name_ = buf;
}
}
主要是start函数
void Thread::start()
{
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);
}
}
这里又有一个ThreadData类 作者将线程的运行函数放在全局函数中 这点有点不太明白 通过给类中设置静态函数 传递this指针也可以 以后再想想吧。
这是运行的函数 此处的CountDownLatch值得注意
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;
}
下面是我参考作者的Thread写的一个程序 不过没有CountDownLatch 当时觉得没必要 接下来就凉凉 出bug了…
#ifndef __THREAD_H
#define __THREAD_H
#include <unistd.h>
#include <sys/syscall.h>
#include<iostream>
#include<functional>
#include<atomic>
namespace mynamespace{
class Thread{
public:
using Threadfunc =std::function<void()>;
Thread(Threadfunc func,std::string &&name="");
~Thread();
void started();
std::string getName();
pid_t getTid();
void joined();
private:
pid_t getid();
void init();
Threadfunc runThread;
static void *callback(void *arg);
pthread_t pthread_id_; //
std::string name_;
pid_t tid_; //
bool started_;
bool joined_;
static std::atomic_int num_;
};
// std::atomic_int Thread::num_(0);
}
#endif
#include"Thread.h"
namespace mynamespace{
Thread::Thread(Threadfunc func,std::string&&name)
:name_(std::move(name))
,runThread(std::move(func))
,started_(false)
,joined_(false)
,pthread_id_(0)
,tid_(0){
init();
}
std::atomic_int Thread::num_(0);
void Thread::started(){
started_=true;
if(pthread_create(&pthread_id_,
NULL,callback,this)){
started_=false;
perror("pthread_create error");
}else{
}
}
void Thread::joined(){
if(started_&&!joined_){
joined_=true;
pthread_join(pthread_id_,NULL);
}
}
pid_t Thread::getid(){
return static_cast<pid_t>(syscall(SYS_gettid));
}
pid_t Thread::getTid(){
return tid_;
}
std::string Thread::getName(){
return name_;
}
Thread::~Thread(){
if(started_&&!joined_){
pthread_detach(pthread_id_);
}
}
void Thread::init(){
int id=num_.fetch_add(1);
if(name_.empty()){
char buf[30];
snprintf(buf,sizeof(buf),"Thread%d\n",id);
name_=buf;
}
}
void *Thread::callback(void *arg){
Thread *thr=(Thread*)arg;
thr->tid_=thr->getTid();
thr->runThread();
}
}
测试代码
#include<iostream>
#include<functional>
#include"Thread.h"
using namespace std;
using namespace mynamespace;
void threadfunc1(){
}
void threadfunc2(int i){
}
int main(){
Thread t1(bind(threadfunc1),"My first thread");
cout<<"t1 name"<<t1.getName()<<endl;
t1.started();
cout<<"t1.Tid"<<t1.getTid()<<endl;
int i=10;
Thread t2(bind(threadfunc2,ref(i)),"Second thread");
t2.started();
cout<<"t2.Tid"<<t2.getTid()<<endl;
}
结果
在我加上printf大法后 出现了这种情况
可知线程还未启动主线程先输出了tid的值
可使线程创建后阻塞在此处 获取tid后再唤醒
在runInThread种 CurrentThread是作者用gcc提供的__thread线程局部数据 进行优化
这是CurentThread原型
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()
{
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();
void sleepUsec(int64_t usec); // for testing
string stackTrace(bool demangle);
} // namespace CurrentThread
} // namespace muduo
#endif // MUDUO_BASE_CURRENTTHREAD_H
其中__builtin_expect(!!(x),y) 表示期望x等于后面的y
!!表示所有非0值都变为1
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) ``__builtin_expect(!!(x), 0)
其中tid函数如下
inline int tid()
{
if (__builtin_expect(t_cachedTid == 0, 0))
{
cacheTid();
}
return t_cachedTid;
}
表明我们希望t_cahedTid不为0 此时就不再需要调用chcheTid 此函数调用了syscall系统调用 不用每次切换到内核去获取tid
这玩意真绕 …
注意点:
__thread线程本地存储
线程本地存储(TLS)是一种机制,通过该机制分配变量,以便每个现有线程都有一个变量实例。 GCC用于实现此目标的运行时模型起源于IA-64特定于处理器的ABI,但此后也已迁移到其他处理器。它需要链接器(ld),动态链接器(ld.so)和系统库(libc.so和libpthread.so)的大力支持,因此并非到处都有它。
在用户级别,可以使用新的存储类关键字__thread看到扩展。例如:
__thread int i;
extern __thread结构状态s;
静态__thread char * p;
__thread说明符可以与extern或static说明符一起单独使用,但不能与其他存储类说明符一起使用。与extern或static一起使用时,__thread必须立即出现在其他存储类说明符之后。
__thread说明符可以应用于类的任何全局,文件作用域的静态,函数作用域的静态或静态数据成员。它可能不适用于块作用域自动或非静态数据成员。
将address-of运算符应用于线程局部变量时,将在运行时对其求值,并返回该变量的当前线程实例的地址。这样获得的地址可以被任何线程使用。当线程终止时,指向该线程中线程局部变量的任何指针都将变为无效。
静态初始化不能引用线程局部变量的地址。
在C ++中,如果存在用于线程局部变量的初始化程序,则该初始化程序必须为常量表达式,如ANSI / ISO C ++标准的5.19.2所定义。
其中
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);
}
};
针对多线程使用了fork
pthread_atfork()函数做清理锁的工作
再来看一个Threadlocal类
template<typename T>
class ThreadLocal : noncopyable
{
public:
ThreadLocal()
{
MCHECK(pthread_key_create(&pkey_, &ThreadLocal::destructor));
}
~ThreadLocal()
{
MCHECK(pthread_key_delete(pkey_));
}
T& value()
{
T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_));
if (!perThreadValue)
{
T* newObj = new T();
MCHECK(pthread_setspecific(pkey_, newObj));
perThreadValue = newObj;
}
return *perThreadValue;
}
private:
static void destructor(void *x)
{
T* obj = static_cast<T*>(x);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void) dummy;
delete obj;
}
private:
pthread_key_t pkey_;
};
pthread_key_create(pthread_key_t *keyp,
void (destor)(void));
创建与线程关联的键 destor正如c++的析构函数做清理工作 线程退出则会被调用
pthread_key_delete(pthread_key_t key)
取消键和线程特定数据的关联关系但并不会调用析构函数
int pthread_setspecific(pthread_key_t key,const void *value)
关联键和线程特定数据
void *pthread_getspecific(pthread_key_t key)
没有线程特定数据与键关联 则返回空指针