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_; //原子操作};}#endifThread.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
- muduo库Thread类剖析
- muduo::Thread类分析
- muduo库的Timestamp类剖析
- muduo库的Singleton类剖析
- muduo库的ThreadLocal类剖析
- muduo库的ThreadLocalSingleton类剖析
- muduo库的PollPoller类剖析
- muduo库的EventLoop类剖析
- muduo库的EventLoopThread类剖析
- muduo库的Acceptor类剖析
- muduo库的ThreadPool剖析
- muduo库的AtomicIntegerT剖析
- muduo库的EpollPoller剖析
- muduo的http库剖析
- muduo的Mutex类剖析
- muduo的Channel类剖析
- muduo网络库学习笔记(3):Thread类
- muduo : Thread
- Mysql必知必会(笔记)【存储过程】
- Elasticsearch.The.Definitive.Guide学习笔记 -- 3. Data in, Data in
- 浅谈Java集合
- **[Lintcode]Majority Number III
- 111.苹果开发者账号和邓白氏编码申请总结
- muduo库Thread类剖析
- 【计算机视觉】背景建模之PBAS
- dirichlet process
- 营养与蛋白质
- 转战、
- 网页扫雷(简易版)(一)
- 动态规划详解附题目。作者地址
- 快速排序的递归和非递归实现
- 【计算机视觉】基于自组织背景减除的运动目标检测算法