muduo库Thread类剖析

来源:互联网 发布:南风知我意1免费阅读 编辑:程序博客网 时间:2024/05/21 19:39

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




0 0
原创粉丝点击