ThreadBase和BaseContext
class ThreadPool拥有很多ThreadBase,而每个ThreadBase是继承自BaseContext。
ThreadBase : BaseContext { ... std::unique_ptr<std::thread> thd_; //这个thd_用来保存实际的线程 ... } void ThreadBase::work() noexcept { pm::common::blockThreadSignals(); //屏蔽信号 this->ready_ = true; dispatch(); //这里调用了event_base_loop(base(), 0); //因此这个ThreadBase里面的event_base就和start()里的线程联系起来了 //因为这个work()函数就是在这个创建的线程里运行的 } void ThreadBase::start() { //线程的回调函数是一个lambda函数 //用这个线程去回调work thd_.reset(new std::thread([this] { this->work(); })); }
在一个线程停止运行时,有两种情况,一种是要销毁这个ThreadBase,另一种是线程与当前线程对象分离,但不销毁ThreadBase
ThreadBase::~ThreadBase() { if (thd_) { this->post([this] { stop(); }); //唤醒thd_去执行停止事件循环的函数 thd_->join(); } } void ThreadBase::end() { this->post([this] { stop(); }); thd_->join(); thd_.reset(); }
感觉调用ThreadBase的成员函数(除了work)是在另一个线程里,然后通过post函数通知thd_处理。
============================================================================
在BaseContext里面,主要有三个成员变量,
// 这个base主要存储了一个event_base, 为线程事件(比如定时器事件)提供一个reactor // 像定时器事件是注册在这个base_中的 std::unique_ptr<struct event_base, std::function<void(struct event_base *)>> base_; std::unique_ptr<TimerQueue> timer_queue_; // 每个线程都有一个定时器 std::unique_ptr<Strand> strand_; // 这个提供了线程处理的机制
这里的base_是和在ThreadBase的thd_线程里进入事件循环的(thd_调用了dispatch, dispatch里有event_base_loop(base(), 0);)
注意一下inMainThread()函数
/** * 如果dispatching,说明该线程正在执行; 因此需要判断线程ID是不是一样 */ bool inMainThread() const { return !dispatching_ || std::this_thread::get_id() == dispatch_thread_id_; }
比如这个函数会在Strand::post函数中调用
void Strand::post(const std::function<void()> &func, bool defer) { // will be any order problems if called by current thread? if (ctx_.inMainThread() && ctx_.isDispathing() && !defer) { func(); return; } std::lock_guard<std::mutex> lock(post_mutex_); post_queue_.push_back(func); uint64_t chr = 1; #ifndef WIN32 ::write(wevent_fd_, &chr, sizeof chr); #else send(wevent_fd_, (const char *)&chr, sizeof chr, 0); #endif // !WIN32 }
判断语句展开就是:
(!dispatching_ || std::this_thread::get_id() == dispatch_thread_id_) && dispatching && !defer
要注意到std::this_thread::get_id()所在的线程不一定是ThreadBase里的那个thd_。如果一个要执行的函数被post到了当前线程而且当前线程正处于可以运行状态(dispatching为true),那就运行该函数。
比如,LogicServer有一个ThreadPool实例,里面有很多ThreadBase实例,LogicServer的work函数在主线程运行时,从线程池中获得了一个ThreadBase实例,比如
auto &mgr_base = thread_pool_->chooseBestThreadBase();
这个实例里面有一个thd_线程,所以当mgr_base.post(...)时,std::this_thread::get_id()是主线程的id,dispatch_thread_id是thd_的id,两者不相同。
着重说一句,ThreadBase实例对象包含了一个线程对象,但也包含或继承了其他许多东西,这个实例对象可以出现在别的线程中。
Strand中提供了一套线程通信的机制,主要是通过一对读写的socket实现的。post函数向一个“写socket”句柄写入消息,然后libevent事件循环中监听到了“读socket”句柄中"可以读"的事件,调用了相应的回调函数执行post_queue_中的函数任务。
#include "base/logging.hpp" #include "strand.hpp" #include <fcntl.h> #include <memory> #ifndef WIN32 #include <sys/eventfd.h> #include <unistd.h> #endif // !WIN32 #include "base_context.hpp" #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include <Ws2tcpip.h> #include <winsock2.h> #endif // WIN32 #include "base/utils.hpp" namespace pm { namespace common { void gOnEventFdCallback(evutil_socket_t fd, short events, void *userdata) { UNUSED(events); auto strand = static_cast<Strand *>(userdata); uint64_t chr; #ifndef WIN32 auto bytes_read = ::read(fd, &chr, sizeof chr); UNUSED(bytes_read); #else recv(fd, (char *)&chr, sizeof chr, 0); #endif // !WIN32 strand->onTriggerEvent(); } Strand::Strand(BaseContext &ctx) throw(std::system_error) : ctx_(ctx) { #ifndef WIN32 if ((revent_fd_ = wevent_fd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) < 0) { throw std::system_error(TO_ERROR_CODE(errno)); } #else // WSADATA wsaData; // WSAStartup(MAKEWORD(2, 2), &wsaData); evutil_socket_t pair[2] = {-1, -1}; //socketpair //该函数和unix中的socketpair一样,它会产生两个连接socket,一个用于输入,一个用于输出。 //注意,在windows中,只支持family = AF_INET,type = SOCK_STREAM,protocol = 0。 if (evutil_socketpair(AF_INET, SOCK_STREAM, 0, pair) < 0) { throw std::system_error(TO_ERROR_CODE(errno)); } if (evutil_make_socket_nonblocking(pair[0]) < 0) { throw std::system_error(TO_ERROR_CODE(errno)); } if (evutil_make_socket_closeonexec(pair[0]) < 0) { throw std::system_error(TO_ERROR_CODE(errno)); } if (evutil_make_socket_nonblocking(pair[1]) < 0) { throw std::system_error(TO_ERROR_CODE(errno)); } if (evutil_make_socket_closeonexec(pair[1]) < 0) { throw std::system_error(TO_ERROR_CODE(errno)); } revent_fd_ = pair[0]; wevent_fd_ = pair[1]; #endif // !WIN32 event_event_ = std::unique_ptr<struct event, std::function<void(struct event *)>>( ::event_new(ctx_.base(), revent_fd_, EV_READ | EV_PERSIST, gOnEventFdCallback, this), [](struct event *ev) { ::event_free(ev); }); if (!event_event_) { throw std::system_error(TO_ERROR_CODE(errno)); } event_add(event_event_.get(), nullptr); } void Strand::post(const std::function<void()> &func, bool defer) { // will be any order problems if called by current thread? if (ctx_.inMainThread() && ctx_.isDispathing() && !defer) { func(); return; } std::lock_guard<std::mutex> lock(post_mutex_); post_queue_.push_back(func); uint64_t chr = 1; #ifndef WIN32 ::write(wevent_fd_, &chr, sizeof chr); #else send(wevent_fd_, (const char *)&chr, sizeof chr, 0); #endif // !WIN32 } /** * 当收到消息的时候,onTriggerEvent 会调用 */ void Strand::onTriggerEvent() { auto start = std::chrono::steady_clock::now(); std::chrono::duration<double> idle_time = start - last_trigger_time_; post_queue_t new_queue; { std::lock_guard<std::mutex> lock(post_mutex_); // 这里是把全部任务一次性都执行掉,要不要根据任务的多寡来切分 new_queue.swap(post_queue_); } for (auto &func : new_queue) { func(); } last_trigger_time_ = std::chrono::steady_clock::now(); decltype(idle_time) busy_time = last_trigger_time_ - start; work_load_ = work_load_ * LOAD_FACTOR + (busy_time.count() / (busy_time.count() + idle_time.count())); } } /* common */ } /* pm */
线程池ThreadPool的代码比较简单,需要注意的是
class ThreadPool : public pm::noncopyable, public pm::common::Global<ThreadPool>
这里ThreadPool继承了Global<ThreadPool>,看Global类定义
/** * 实现一个特制的单例。真正的对象是由外部传进来的, * 一般是存在栈上,需要主动调用::set上去 */ #pragma once namespace pm { namespace common { template <typename T> class Global { public: static T *get() {return t_;} static void set(T *t) { t_ = t; } static T &inst() { return *t_; } private: static T *t_; }; template <typename T> T *Global<T>::t_ = nullptr; } /* common */ } /* pm */
这个类在以后还会经常碰到。比如LogicServer的work函数里有一句
pm::common::ThreadPool::set(thread_pool_.get());
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号