天天看點

muduo庫源碼分析(4):線程類

  • 與muduo線程類封裝有關的源檔案:

    Thread.cc Thread.h CurrentThread.h

  • muduo線程類封裝實作方式:

    基于對象的程式設計(利用boost::function/boost::bind實作)

  • 簡化版的線程類封裝可參考:

    http://blog.csdn.net/jacktangj/article/details/76166554

  • muduo線程類整體結構:
muduo庫源碼分析(4):線程類

這裡簡化了成員函數以及全局函數

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);
  }
};
__thread int t_cachedTid;//線程實際ID的緩存,減少多次調用syscall(SYS_gettid)擷取線程實際ID
  __thread char t_tidString[];//線程tid的字元串形式
  __thread int t_tidStringLength;//線程tid的字元串形式的長度
  __thread const char* t_threadName;// 線程的名字
  __thread修飾的變量為每個線程的私有資料,不共享
  隻能修飾POD類型資料(後面注意),非POD資料可用線程的特定資料實作
 
inline int tid()
{
    if (__builtin_expect(t_cachedTid == , ))
    {
      cacheTid();
    }
    return t_cachedTid;
}
__builtin_expect:gcc的編譯優化語句,用于提前加載if的語句還是else語句
void CurrentThread::cacheTid()
{
  if (t_cachedTid == )
  {
    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)
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_(),// 線程ID
    tid_(new pid_t()),// 線程實際ID
    func_(func),// 回調函數
    name_(n)// 線程名
{
  setDefaultName();
}
void Thread::setDefaultName()
{
// 靜态成員變量用于線程計數:原子性操作+1
  int num = numCreated_.incrementAndGet();
  if (name_.empty())
  {
    char buf[];
    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,結構體等),但使用者定義的帶有構造函數和虛函數的類則不是。

繼續閱讀