热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

muduo库Thread类剖析

muduo库中的Thread类集合了所有线程的操作,其中还运用了线程安全的观察者模式。运用shared_ptr和weak_ptr做到了生命周期的管理。线程类的接口:

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

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


线程类的接口:



代码加注释如下:

Thread.h

#ifndef MUDUO_BASE_THREAD_H
#define MUDUO_BASE_THREAD_H

#include 
#include 

#include 
#include 
#include 
#include 

namespace muduo
{

class Thread : boost::noncopyable
{
 public:
  typedef boost::function 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 tid_;    //用来管理生命期
  ThreadFunc func_;                        //线程回调函数
  string     name_;

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

}
#endif
Thread.cc

#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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::value; //boost::is_same用来检测参数是否是相同的类型,相同返回true。
  BOOST_STATIC_ASSERT(sameType);   //断言相同
}

namespace detail
{

pid_t gettid()
{
  return static_cast(::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 wkTid_;     

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

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

    boost::shared_ptr 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(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(usec / Timestamp::kMicroSecondsPerSecond);
  ts.tv_nsec = static_cast(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 
#include 
#include 
#include 

using namespace std;

void prepare()
{
    cout<<"pid = "<(getpid())<<" prepare"<输出: 
 



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

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





推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文总结了在开发中使用gulp时的一些技巧,包括如何使用gulp.dest自动创建目录、如何使用gulp.src复制具名路径的文件以及保留文件夹路径的方法等。同时介绍了使用base选项和通配符来保留文件夹路径的技巧,并提到了解决带文件夹的复制问题的方法,即使用gulp-flatten插件。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
author-avatar
sendymylove睛飘益
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有