muduo网络库学习:reactor模式,EventLoop::runInLoop,多线程
修改Logging.h,使日志中打印的函数显示类名。
static std::string _CutParenthesesNTail(std::string&& prettyFuncon){std::string::size_type pos = prettyFuncon.find('(');
if(pos!=std::string::npos)prettyFuncon.erase(prettyFuncon.begin()+pos, prettyFuncon.end());pos = prettyFuncon.find(':');if(pos!=std::string::npos)prettyFuncon.erase(prettyFuncon.begin(), prettyFuncon.begin()+pos+2);return std::move(prettyFuncon);
}#define __STR_FUNCTION__ _CutParenthesesNTail(std::string(__PRETTY_FUNCTION__))
//muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, (__STR_FUNCTION__).c_str()).stream()
static std::string _CutParenthesesNTail(std::string&& prettyFuncon) { std::string::size_type pos = prettyFuncon.find('('); if(pos!=std::string::npos) prettyFuncon.erase(prettyFuncon.begin()+pos, prettyFuncon.end()); pos = prettyFuncon.find(':'); if(pos!=std::string::npos) prettyFuncon.erase(prettyFuncon.begin(), prettyFuncon.begin()+pos+2); return std::move(prettyFuncon); } #define __STR_FUNCTION__ _CutParenthesesNTail(std::string(__PRETTY_FUNCTION__)) //muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream() #define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \ muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, (__STR_FUNCTION__).c_str()).stream()
1. runInLoop()和queueInLoop()
通过EventLoop::runInLoop()函数可以在线程间调配任务。
在 muduo 中,平时 IO 线程都阻塞在EventLoop::loop函数的poll函数中, 某一个(IO线程或者其他)线程可以执行一个任务调用EventLoop::runInLoop这个函数, 判断如果当前线程是不是 IO 线程, 如果是就直接执行任务, 不是就将回调函数添加进任务队列, 并唤醒 IO 线程, 让IO线程执行回调函数。muduo中使用eventfd(2),并监听readable事件唤醒线程。
void EventLoop::runInLoop(const Functor& cb){if (isInLoopThread()) {
// 如果是当前IO线程调用runInLoop,则同步调用cb
cb();} else {
// 如果是其它线程调用runInLoop,则异步地将cb添加到队列
queueInLoop(cb);}}void EventLoop::queueInLoop(Functor&& cb)
{{MutexLockGuard lock(mutex_);pendingFunctors_.push_back(cb);}·//唤醒条件
//不是IO线程
//是IO线程但是正在执行任务队列中的任务
if (!isInLoopThread() || callingPendingFunctors_) {
wakeup();}}
唤醒条件:
a.调用queueInLoop的线程不是当前IO线程,则需要唤醒当前IO线程,才能及时执行doPendingFunctors();
或者
b.调用queueInLoop的线程是当前IO线程,并且此时正在调用pending functor,需要唤醒当前IO线程
因为在doPendingFunctors() 过程中又添加了任务cb,(比如在doPendingFunctors()中执行functors[i]() 时又调用了queueInLoop());故EventLoop::loop()循环回去poll的时候需要被唤醒返回,进而才能继续执行doPendingFunctors()及时调用回调函数cb;
换句话说,只有当前IO线程的事件回调handleEvent()中调用queueInLoop才不需要唤醒, 即在handleEvent()中调用queueInLoop 不需要唤醒,接下来马上就会执行doPendingFunctors(),从而执行函数cb;
2.doPendingFunctors()
void EventLoop::loop()
{assert(!looping_);assertInLoopThread();looping_ = true;
quit_ = false;
while (!quit_) {
activeChannels_.clear();pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it) {(*it)->handleEvent();}doPendingFunctors();}LOG_TRACE << "EventLoop " << this << " stop looping";looping_ = false;
}void EventLoop::doPendingFunctors()
{std::vector<Functor> functors;callingPendingFunctors_ = true;
{MutexLockGuard lock(mutex_);functors.swap(pendingFunctors_);}for (size_t i = 0; i < functors.size(); ++i) {
functors[i]();}callingPendingFunctors_ = false;
}
doPendingFunctors函数只会被当前IO线程调用,doPendingFunctors并没有直接在临界区去执行functors,而是利用了一个栈对象,把事件swap到栈对象中,再去执行。这样做有两个好处:
-
减少了临界区的长度,其它线程调用queueInLoop对pendingFunctors加锁时,就不会被阻塞
-
避免了死锁,可能在functors里面也会调用queueInLoop(),从而造成死锁。
muduo在处理多线程加锁访问共享数据的策略上,有一个很重要的原则:拼命减少临界区的长度
试想一下,如果没有pendingFunctors_这个数据成员,我们要想往TimerQueue中添加timer,肯定要对TimerQueue里面的insert函数加锁,造成锁的争用,而pendingFunctors_这个成员将锁的范围减少到了一个vector的push_back操作上。此外,在doPendingFunctors中,利用一个栈对象减少临界区。
3.wake()
static int createEventfd(){int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evtfd < 0) {
LOG_SYSERR << "Failed in eventfd";
abort();}return evtfd;
}void EventLoop::wakeup()
{uint64_t one = 1;ssize_t n = ::write(wakeupFd_, &one, sizeof one);
if (n != sizeof one) {LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";}}EventLoop::EventLoop(): looping_(false),
quit_(false),
callingPendingFunctors_(false),
threadId_(CurrentThread::tid()),poller_(new Poller(this)),timerQueue_(new TimerQueue(this)),wakeupFd_(createEventfd()),wakeupChannel_(new Channel(this, wakeupFd_)){...}
示例代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "EventLoop.h" 2 #include "EventLoopThread.h" 3 #include <stdio.h> 4 5 void runInThread() 6 { 7 printf("runInThread(): pid = %d, tid = %d\n", 8 getpid(), muduo::CurrentThread::tid()); 9 } 10 11 int main() 12 { 13 printf("main(): pid = %d, tid = %d\n", 14 getpid(), muduo::CurrentThread::tid()); 15 16 muduo::EventLoopThread loopThread; 17 muduo::EventLoop* loop = loopThread.startLoop(); 18 loop->runInLoop(runInThread); 19 printf("-----------------------------------\n"); 20 sleep(1); 21 loop->runAfter(2, runInThread); 22 sleep(3); 23 loop->quit(); 24 25 printf("exit main().\n"); 26 }
输出结果(删除一些输出信息)
1 main(): pid = 8311, tid = 8311 2 05:16.990333Z 8312 TimerQueue::TimerQueue TimerQueue ctor timerfd_ = 3 - TimerQueue.cc:90 3 05:16.993679Z 8312 EventLoop::updateChannel - EventLoop.cc:148 4 05:16.993696Z 8312 Poller::updateChannel fd = 3 events = 3 - Poller.cc:66 5 05:16.993728Z 8312 EventLoop::EventLoop EventLoop created 0x7f01f96f5a80 in thread 8312 - EventLoop.cc:47 6 05:16.993738Z 8312 EventLoop::updateChannel - EventLoop.cc:148 7 05:16.993743Z 8312 Poller::updateChannel fd = 4 events = 3 - Poller.cc:66 8 05:16.993961Z 8311 EventLoop::queueInLoop cb = 0x7ffd76a03330 - EventLoop.cc:117 9 05:16.994000Z 8311 EventLoop::wakeup wakeupFd_ = 4 - EventLoop.cc:163 10 ----------------------------------- 11 05:16.994092Z 8312 Timestamp muduo::Poller::poll 1 events happended - Poller.cc:33 12 05:16.994184Z 8312 EventLoop::loop (*it)->fd() = 4 - EventLoop.cc:84 13 05:16.994197Z 8312 EventLoop::handleRead wakeupFd_ = 4 - EventLoop.cc:174 14 05:16.994211Z 8312 EventLoop::doPendingFunctors -----begin - EventLoop.cc:185 15 runInThread(): pid = 8311, tid = 8312 16 05:16.994225Z 8312 EventLoop::doPendingFunctors -----end - EventLoop.cc:199 17 ***************2222222222**************** 18 19 timerfd_ = 3 wakeupFd_ = 4 loop->runAfter(2, runInThread); 20 loop_->runInLoop( 21 boost::bind(&TimerQueue::addTimerInLoop, this, timer)); 22 05:17.994600Z 8311 EventLoop::queueInLoop cb = 0x7ffd76a03170 - EventLoop.cc:117 23 05:17.994637Z 8311 EventLoop::wakeup wakeupFd_ = 4 - EventLoop.cc:163 24 ###########3333333333333333####### 25 05:17.994853Z 8312 Timestamp muduo::Poller::poll 1 events happended - Poller.cc:33 26 05:17.994908Z 8312 EventLoop::loop (*it)->fd() = 4 - EventLoop.cc:84 27 05:17.994924Z 8312 EventLoop::handleRead wakeupFd_ = 4 - EventLoop.cc:174 28 05:17.994939Z 8312 EventLoop::doPendingFunctors -----begin - EventLoop.cc:185 29 TimerQueue::addTimerInLoop 30 05:17.994955Z 8312 TimerQueue::insert - TimerQueue.cc:193 31 05:17.994989Z 8312 EventLoop::doPendingFunctors -----end - EventLoop.cc:199 32 05:19.995398Z 8312 Timestamp muduo::Poller::poll 1 events happended - Poller.cc:33 33 05:19.995460Z 8312 EventLoop::loop (*it)->fd() = 3 - EventLoop.cc:84 34 05:19.995474Z 8312 TimerQueue::handleRead - TimerQueue.cc:131 35 05:19.995509Z 8312 detail::readTimerfd TimerQueue::handleRead() 1 at 1556550319.995483 - TimerQueue.cc:56 36 05:19.995529Z 8312 muduo::TimerQueue::getExpired - TimerQueue.cc:150 37 runInThread(): pid = 8311, tid = 8312 //loop->runAfter(2, runInThread); 回调函数 38 05:19.995564Z 8312 EventLoop::doPendingFunctors -----begin - EventLoop.cc:185 39 05:19.995576Z 8312 EventLoop::doPendingFunctors -----end - EventLoop.cc:199 40 ----------------------------------- 41 05:20.995376Z 8311 EventLoop::wakeup wakeupFd_ = 4 - EventLoop.cc:163 42 exit main(). 43 05:20.995480Z 8311 EventLoop::wakeup wakeupFd_ = 4 - EventLoop.cc:163 44 05:20.995615Z 8312 Timestamp muduo::Poller::poll 1 events happended - Poller.cc:33 45 05:20.995670Z 8312 EventLoop::loop (*it)->fd() = 4 - EventLoop.cc:84 46 05:20.995686Z 8312 EventLoop::handleRead wakeupFd_ = 4 - EventLoop.cc:174 47 05:20.995701Z 8312 EventLoop::doPendingFunctors -----begin - EventLoop.cc:185 48 05:20.995715Z 8312 EventLoop::doPendingFunctors -----end - EventLoop.cc:199 49 05:20.995725Z 8312 EventLoop::loop EventLoop 0x7f01f96f5a80 stop looping - EventLoop.cc:90
线程8311 执行 loop->runInLoop(runInThread);对应输出信息第8行,第9行
8311 EventLoop::queueInLoop cb = 0x7ffd5111ff50 - EventLoop.cc:116
8311 EventLoop::wakeup wakeupFd_ = 4 - EventLoop.cc:162
之后唤醒IO线程8312,执行回调函数,输出信息 第11-15行
8312 Timestamp muduo::Poller::poll 1 events happended - Poller.cc:33
8312 EventLoop::handleRead wakeupFd_ = 4 - EventLoop.cc:173
8312 EventLoop::doPendingFunctors - EventLoop.cc:184
runInThread(): pid = 8311, tid = 8312