muduo库源码分析(4):线程类

  • 与muduo线程类封装有关的源文件:
    Thread.cc Thread.h CurrentThread.h
  • muduo线程类封装实现方式:
    基于对象的编程(利用boost::function/boost::bind实现)
  • 简化版的线程类封装可参考:
    http://blog.csdn.net/jacktangj/article/details/76166554
  • muduo线程类整体结构:

muduo线程类整体结构:
这里简化了成员函数以及全局函数
Thread类:线程类
CurrentThread命名空间:声明每个线程的私有数据
ThreadNameInitializer 类:用于初始化主线程的线程特有数据
ThreadData结构体:通过回调函数传递给子线程的数据

简单分析Thread类的运行过程。

  1. detail命名空间有全局变量init,会首先调用ThreadNameInitializer 类构造函数。以及包含静态成员numCreate_(原子类Automic)的初始化
    ThreadNameInitializer 类结构
class ThreadNameInitializer
{
 public:
  ThreadNameInitializer()
  {
    muduo::CurrentThread::t_threadName = "main";
    CurrentThread::tid();
    pthread_atfork(NULL, NULL, &afterFork);
  }
};
1.__thread int t_cachedTid;//线程实际ID的缓存,减少多次调用syscall(SYS_gettid)获取线程实际ID
  __thread char t_tidString[32];//线程tid的字符串形式
  __thread int t_tidStringLength;//线程tid的字符串形式的长度
  __thread const char* t_threadName;// 线程的名字
  __thread修饰的变量为每个线程的私有数据,不共享
  只能修饰POD类型数据(后面注意2),非POD数据可用线程的特定数据实现
2. 
inline int tid()
{
    if (__builtin_expect(t_cachedTid == 0, 0))
    {
      cacheTid();
    }
    return t_cachedTid;
}
__builtin_expect:gcc的编译优化语句,用于提前加载if的语句还是else语句
void CurrentThread::cacheTid()
{
  if (t_cachedTid == 0)
  {
    t_cachedTid = detail::gettid();
    t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
  }
}
pid_t gettid()
{
  return static_cast<pid_t>(::syscall(SYS_gettid));
}
syscall(SYS_gettid)//获取线程的实际ID(后面注意1)
3.pthread_atfork(prepare,parent,child)
在fork之前执行prepare函数,fork后主进程执行parent,子进程执行child
注意:多线程程序最好不要去fork,很容易造成死锁

2.构造Thread类的构造函数(Thread t(threadFunc);)

Thread::Thread(const ThreadFunc& func, const string& n)
  : started_(false),//标记线程是否启动
    joined_(false),// 标记线程是否连接,即pthread_join
    pthreadId_(0),// 线程ID
    tid_(new pid_t(0)),// 线程实际ID
    func_(func),// 回调函数
    name_(n)// 线程名
{
  setDefaultName();
}
void Thread::setDefaultName()
{
// 静态成员变量用于线程计数:原子性操作+1
  int num = numCreated_.incrementAndGet();
  if (name_.empty())
  {
    char buf[32];
    snprintf(buf, sizeof buf, "Thread%d", num);
    name_ = buf;
  }
}

3.执行(t.start();t.join();)

void Thread::start()
{
  assert(!started_);
  started_ = true;
  detail::ThreadData* data = new detail::ThreadData(func_, name_, tid_);
  // 创建线程
  if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
  {
    started_ = false;
    delete data; // or no delete?
    LOG_SYSFATAL << "Failed in pthread_create";//日志文件输出(后续)
  }
}
void* startThread(void* obj)
{
  ThreadData* data = static_cast<ThreadData*>(obj);
  data->runInThread();
  delete data;
  return NULL;
}

注意:
1.线程标识符:
进程———-线程
pid_t———-pthread_t
getpid()———-pthread_self()
标识符唯一———-不同进程拥有相同线程ID
linux中posix线程实现也是一个轻量级进程,只是该进程与主进程共享一些资源,有时需要实际的线程ID,例如p1->p2某个线程时,既不能用p2的pid也不能用改线程的pthread_self(),只能用线程的实际ID,获取方式调用:syscall(SYS_gettid)
2.POD类型:与C兼容的数据类型(int,double,结构体等),但用户定义的带有构造函数和虚函数的类则不是。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章