muduo库Thread类剖析

muduo库中的Thread类集合了所有线程的操作,其中还运用了线程安全的观察者模式。

运用shared_ptr和weak_ptr做到了生命周期的管理。


线程类的接口:



代码加注释如下:

Thread.h

#ifndef MUDUO_BASE_THREAD_H
#define MUDUO_BASE_THREAD_H

#include <muduo/base/Atomic.h>
#include <muduo/base/Types.h>

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <pthread.h>

namespace muduo
{

class Thread : boost::noncopyable
{
 public:
  typedef boost::function<void ()> ThreadFunc;

  explicit Thread(const ThreadFunc&, const string& name = string());
#ifdef __GXX_EXPERIMENTAL_CXX0X__
  explicit Thread(ThreadFunc&&, const string& name = string()); //右值引用,在对象返回的时候不会拷贝构造临时对象,而是和临时对象交换,提高了效率
#endif
  ~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_; }   //由于pthread_t的id号可能是一样的,所以需要用gettid()
  const string& name() const { return name_; }

  static int numCreated() { return numCreated_.get(); }   

 private:
  void setDefaultName();

  bool       started_;
  bool       joined_;
  pthread_t  pthreadId_;
  boost::shared_ptr<pid_t> tid_;    //用来管理生命期
  ThreadFunc func_;                        //线程回调函数
  string     name_;

  static AtomicInt32 numCreated_;   //原子操作
};

}
#endif
Thread.cc

#include <muduo/base/Thread.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Exception.h>
#include <muduo/base/Logging.h>

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/weak_ptr.hpp>

#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 int t_cachedTid = 0;      //用来缓存的id  //__thread修饰的变量是线程局部存储的,线程不共享,线程安全,解释见下2
  __thread char t_tidString[32];     //tid的字符串表示形式   
  __thread int t_tidStringLength = 6;   
  __thread const char* t_threadName = "unknown";   //每个线程的名称
  const bool sameType = boost::is_same<int, pid_t>::value; //boost::is_same用来检测参数是否是相同的类型,相同返回true。
  BOOST_STATIC_ASSERT(sameType);   //断言相同
}

namespace detail
{

pid_t gettid()
{
  return static_cast<pid_t>(::syscall(SYS_gettid));     //系统调用获取  //解释见下1
}

void afterFork()        //fork之后打扫战场,子进程中执行
{
  muduo::CurrentThread::t_cachedTid = 0;    //当前为0   //1.先清零tid
  muduo::CurrentThread::t_threadName = "main";//为main//为什么要赋值为0和main,因为fork可能在主线程中调用,也可能在子线程中调用。fork得到一个新进程,
  CurrentThread::tid();        //2.此处再缓存tid               //新进程只有一个执行序列,只有一个线程
  // no need to call pthread_atfork(NULL, NULL, &afterFork);   //实际上服务器要么多进程,要么多线程。如果都用,甚至可能死锁,见下5
}

class ThreadNameInitializer   //线程名初始化
{
 public:
  ThreadNameInitializer()
  {
    muduo::CurrentThread::t_threadName = "main";   //由下面的init全局对象先触发构造,主线程的名称为main
    CurrentThread::tid();                    //获得tid
    pthread_atfork(NULL, NULL, &afterFork);    //如果我们调用了fork函数,调用成功后子进程会调用afterfork()
  }
};

ThreadNameInitializer init;  //全部变量类,这个对象构造先于main函数,当我们的程序引入这个库时,这个全局函数直接构造,我们程序的main()函数还没有执行。

struct ThreadData    //线程数据类,观察者模式
{
  typedef muduo::Thread::ThreadFunc ThreadFunc;   
  ThreadFunc func_;
  string name_;
  boost::weak_ptr<pid_t> wkTid_;     

  ThreadData(const ThreadFunc& func,
             const string& name,
             const boost::shared_ptr<pid_t>& tid)    //保存有Thread类的shared_ptr
    : func_(func),
      name_(name),
      wkTid_(tid)
  { }

  void runInThread()    //线程运行
  {
    pid_t tid = muduo::CurrentThread::tid();  //得到线程tid

    boost::shared_ptr<pid_t> ptid = wkTid_.lock();  //尝试将weak_ptr提升为shared_ptr,thread_safe
    if (ptid)     //如果成功
    {
      *ptid = tid;    
      ptid.reset();   //成功了之后把通过智能指针修改父类线程id,该临时shared_ptr销毁掉
    }

    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";   //运行玩的threadname
    }
    catch (const Exception& ex)   //Exception异常
    {
      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);  //派生类指针转化成基类指针,obj是派生类的this指针
  data->runInThread();
  delete data;
  return NULL;
}

}
}

using namespace muduo;

void CurrentThread::cacheTid()   //在这里第一次会缓存tid,并不会每次都systemcall,提高了效率
{
  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::Thread(const ThreadFunc& func, const string& n)
  : started_(false),
    joined_(false),
    pthreadId_(0),
    tid_(new pid_t(0)),
    func_(func),
    name_(n)
{
  setDefaultName();
}

#ifdef __GXX_EXPERIMENTAL_CXX0X__     //C++11标准
Thread::Thread(ThreadFunc&& func, const string& n)
  : started_(false),
    joined_(false),
    pthreadId_(0),
    tid_(new pid_t(0)),
    func_(std::move(func)),
    name_(n)
{
  setDefaultName();
}

#endif
//pthread_join()和pthread_detach()都是防止现成资源泄露的途径,join()会阻塞等待。
Thread::~Thread()    //这个析构函数是线程安全的。析构时确认thread没有join,才会执行析构。即线程的析构不会等待线程结束
{                                    //如果thread对象的生命周期长于线程,那么可以通过join等待线程结束。否则thread对象析构时会自动detach线程,防止资源泄露
  if (started_ && !joined_)   //如果没有join,就detach,如果用过了,就不用了。
  {
    pthread_detach(pthreadId_);
  }
}

void Thread::setDefaultName()   //相当于给没有名字的线程起个名字 "Thread %d"
{
  int num = numCreated_.incrementAndGet();  //原子操作
  if (name_.empty())
  {
    char buf[32];
    snprintf(buf, sizeof buf, "Thread%d", num);
    name_ = buf;
  }
}

void Thread::start()   //线程启动
{
  assert(!started_);
  started_ = true;
  // FIXME: move(func_)
  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";
  }
}

int Thread::join()    //等待线程
{
  assert(started_);
  assert(!joined_);
  joined_ = true;
  return pthread_join(pthreadId_, NULL);
}

需要注意的是

1.在linux系统中,每个进程都有一个pid,类型pid_t,由getpid()取得。Linux下的POSIX线程也有一个id,类型pthread_t,由pthread_self()取得,该线程由线程库维护,其id空间是各个进程独立的(即不同线程中可能拥有相同的id)。有时我们需要知道线程的真实id,就不能使用pid和pthread id,需要使用该线程真实的id,称为tid。(不是我们平常所用的pthread_t tid; tid=pthread_create()的那个tid)。

linux系统中有一个系统调用可以实现得到线程的真实id,我们可以实现一个函数,返回该系统调用的返回值,return syscall(SYS_gettid),但是频繁系统调用会造成性能降低,muduo库就使用了全局变量current_tid来缓存它,我们只需要调用一次,以后直接获取该缓存就可以了。


2.__thread修饰的变量是线程局部存储的,每个线程都有一份。__thread,gcc内置的线程局部存储设施。__thread只能修饰POD类型。如果类没有定义构造函数,也是POD类型。


3.boost::is_same用来判断是否是同一类型

比如typedef unsigned int u_int,就是同一类型。


4.pthread_atfork()

int pthread_atfork(void (*prepare)(void), void(*parent)(void), void(*child)(void));

调用fork时,内部创建子进程前在父进程中会调用prepare,内部创建子进程成功后,父进程会调用parent,子进程会调用child。

示例:

#include <iostream>
#include <time.h>
#include <pthread.h>
#include <unistd.h>

using namespace std;

void prepare()
{
    cout<<"pid = "<<static_cast<int>(getpid())<<" prepare"<<endl;
}

void parent()
{
    cout<<"pid = "<<static_cast<int>(getpid())<<" parent"<<endl;
}

void child()
{
    cout<<"pid = "<<(static_cast<int>(getpid()))<<" child"<<endl;
}

int main()
{
    cout<<"pid = "<<(static_cast<int>(getpid()))<<" enntering main"<<endl;

    pthread_atfork(prepare, parent, child);

    fork();

    cout<<"pid = "<<(static_cast<int>(getpid()))<<" exiting main"<<endl;

    return 0;
}
输出:



5.多线程程序中调用fork()造成死锁示例

见我另一篇博客,这部分是我追加的:http://blog.csdn.net/freeelinux/article/details/53426876




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值