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到栈对象中,再去执行。这样做有两个好处:
  1. 减少了临界区的长度,其它线程调用queueInLoop对pendingFunctors加锁时,就不会被阻塞

  2. 避免了死锁,可能在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_))
{
	...
}

 

 

示例代码

 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 }
View Code

输出结果(删除一些输出信息)

 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

posted on 2019-04-29 22:05  flysong  阅读(429)  评论(0编辑  收藏  举报

导航