EventLoop再分析之IO线程(29)
来源:互联网 发布:福州seo基础培训 编辑:程序博客网 时间:2024/06/02 09:41
EventLoop IO 线程的简单描述
进程(线程)wait/notify
pipe
socketpair
eventfd
eventfd 是一个比 pipe 更高效的线程间事件通知机制,一方面它比 pipe 少用一个 file descripor,节省了资源;另一方面,eventfd 的缓冲区管理也简单得多,全部“buffer” 只有定长8 bytes,不像 pipe 那样可能有不定长的真正 buffer。
EventLoop头文件
eventloop.h
这里的eventloop 和 “muduo_net库源码分析(26-1)”里面的内容有些不一样,这里的eventloop加了一些内容
在阅读eventloop时,要注意runInLoop和queueInloop这两个函数,wakeup()也得注意
// Copyright 2010, Shuo Chen. All rights reserved.// http://code.google.com/p/muduo///// Use of this source code is governed by a BSD-style license// that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com)//// This is a public header file, it must only include public header files. #ifndef MUDUO_NET_EVENTLOOP_H#define MUDUO_NET_EVENTLOOP_H #include <vector> #include <boost/noncopyable.hpp>#include <boost/scoped_ptr.hpp> #include <muduo/base/Mutex.h>#include <muduo/base/Thread.h>#include <muduo/base/Timestamp.h>#include <muduo/net/Callbacks.h>#include <muduo/net/TimerId.h> namespace muduo{namespace net{ class Channel;class Poller;class TimerQueue;////// Reactor, at most one per thread.////// This is an interface class, so don't expose too much details.class EventLoop : boost::noncopyable{ public: /* IO事件回调函数,就像“监听socketfd” ,我们可以把 监听socketfd 的处理函数 做成一个回调函数, 把这个函数放到pendingFunctors_中,然后由IO线程自己回调处理*/ typedef boost::function<void()> Functor; EventLoop(); ~EventLoop(); // force out-line dtor, for scoped_ptr members. /// /// Loops forever. /// /// Must be called in the same thread as creation of the object. /// void loop(); void quit(); /// /// Time when poll returns, usually means data arrivial. /// Timestamp pollReturnTime() const { return pollReturnTime_; } /// Runs callback immediately in the loop thread. /// It wakes up the loop, and run the cb. /// If in the same loop thread, cb is run within the function. /// Safe to call from other threads. void runInLoop(const Functor& cb); /// Queues callback in the loop thread. /// Runs after finish pooling. /// Safe to call from other threads. void queueInLoop(const Functor& cb); // timers /// /// Runs callback at 'time'. /// Safe to call from other threads. /// TimerId runAt(const Timestamp& time, const TimerCallback& cb); /// /// Runs callback after @c delay seconds. /// Safe to call from other threads. /// TimerId runAfter(double delay, const TimerCallback& cb); /// /// Runs callback every @c interval seconds. /// Safe to call from other threads. /// TimerId runEvery(double interval, const TimerCallback& cb); /// /// Cancels the timer. /// Safe to call from other threads. /// void cancel(TimerId timerId); // internal usage void wakeup(); void updateChannel(Channel* channel); // 在Poller中添加或者更新通道 void removeChannel(Channel* channel); // 从Poller中移除通道 void assertInLoopThread() { if (!isInLoopThread()) { abortNotInLoopThread(); } } bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); } bool eventHandling() const { return eventHandling_; } static EventLoop* getEventLoopOfCurrentThread(); private: void abortNotInLoopThread(); void handleRead(); // waked up void doPendingFunctors(); void printActiveChannels() const; // DEBUG typedef std::vector<Channel*> ChannelList; bool looping_; /* atomic */ bool quit_; /* atomic */ bool eventHandling_; /*事件处理函数状态 atomic */ bool callingPendingFunctors_; /*是否处于IO回调函数的处理状态中 atomic */ const pid_t threadId_; // 当前对象所属线程ID Timestamp pollReturnTime_; boost::scoped_ptr<Poller> poller_; boost::scoped_ptr<TimerQueue> timerQueue_; /***** wakeupFd_ : 这个描述符主要是未了 ****/ int wakeupFd_; // 用于eventfd所创建的文件描述符 // unlike in TimerQueue, which is an internal class, // we don't expose Channel to client. /* wakeupChannel 和EventLoop 是组合的关系,生命周期由EventLoop管理 一定要看下面的内容,如果不明白下面的内容的话,代码是很难懂的(个人观点,高手勿喷!!^V^) 当其他线程跟IO线程通信时, 他们使用的是同一个通道(wakeupChannel_) , 当其他线程wakeup(write)时,poll会返回。然后IO执行handleRead,但是主要的回调函数不是handleRead ,而是pendingFunctors_里面的函数,hadnleRead只是为了不被多次触发而已,也就是说其他线程要想IO线程执行他们所需的函调函数,只要把回调函数注入到pendingFunctors_,然后wakeup(write)唤醒一下。 当然IO线程也可以和IO线程进行通信,但是情况有些不一样,具体哪些不同,大家自己看着办吧! 注: IO线程就是EventLoop所属的线程 */ boost::scoped_ptr<Channel> wakeupChannel_; //wakeupFd_所对应的通道 该通道将会纳入poller_来管理 ChannelList activeChannels_; // Poller返回的活动通道 Channel* currentActiveChannel_; // 当前正在处理的活动通道 MutexLock mutex_; // IO线程 回调函数容器 std::vector<Functor> pendingFunctors_; // @BuardedBy mutex_}; }}#endif // MUDUO_NET_EVENTLOOP_H
EventLoop源文件
这里的eventloop 和 “muduo_net库源码分析(26-1)”里面的内容有些不一样,这里的eventloop加了一些内容,具体情况可以回去看一看
// Copyright 2010, Shuo Chen. All rights reserved.// http://code.google.com/p/muduo///// Use of this source code is governed by a BSD-style license// that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) #include <muduo/net/EventLoop.h> #include <muduo/base/Logging.h>#include <muduo/net/Channel.h>#include <muduo/net/Poller.h>#include <muduo/net/TimerQueue.h> //#include <poll.h>#include <boost/bind.hpp> #include <sys/eventfd.h> using namespace muduo;using namespace muduo::net; namespace{// 当前线程EventLoop对象指针// 线程局部存储__thread EventLoop* t_loopInThisThread = 0; const int kPollTimeMs = 10000; int createEventfd(){ int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (evtfd < 0) { LOG_SYSERR << "Failed in eventfd"; abort(); } return evtfd;} } EventLoop* EventLoop::getEventLoopOfCurrentThread(){ return t_loopInThisThread;} EventLoop::EventLoop() : looping_(false), quit_(false), eventHandling_(false), callingPendingFunctors_(false), threadId_(CurrentThread::tid()), poller_(Poller::newDefaultPoller(this)), timerQueue_(new TimerQueue(this)), wakeupFd_(createEventfd()), //创建 wakeupFd_ wakeupChannel_(new Channel(this, wakeupFd_)), //创建wakeupFd_对应的channel currentActiveChannel_(NULL){ LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_; // 如果当前线程已经创建了EventLoop对象,终止(LOG_FATAL) if (t_loopInThisThread) { LOG_FATAL << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_; } else { t_loopInThisThread = this; } // 设定wakeupChannel的回调函数 wakeupChannel_->setReadCallback( boost::bind(&EventLoop::handleRead, this)); // we are always reading the wakeupfd //纳入poller来管理 wakeupChannel_->enableReading();} EventLoop::~EventLoop(){ ::close(wakeupFd_); t_loopInThisThread = NULL;} // 事件循环,该函数不能跨线程调用// 只能在创建该对象的线程中调用void EventLoop::loop(){ assert(!looping_); // 断言当前处于创建该对象的线程中 assertInLoopThread(); looping_ = true; quit_ = false; LOG_TRACE << "EventLoop " << this << " start looping"; //::poll(NULL, 0, 5*1000); while (!quit_) { activeChannels_.clear(); pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); //++iteration_; if (Logger::logLevel() <= Logger::TRACE) { printActiveChannels(); } // TODO sort channel by priority eventHandling_ = true; for (ChannelList::iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it) { currentActiveChannel_ = *it; currentActiveChannel_->handleEvent(pollReturnTime_); } currentActiveChannel_ = NULL; eventHandling_ = false; // 让IO线程也能执行一些计算任务 doPendingFunctors(); } LOG_TRACE << "EventLoop " << this << " stop looping"; looping_ = false;} // 该函数可以跨线程调用void EventLoop::quit(){ quit_ = true; //如果不是由当前IO线程调用的,那么我们还要唤醒IO线程 if (!isInLoopThread()) { wakeup(); }}/*sublime 注释的好方法,都是苦力活=========== =================== ====== ===================|| ||| |||================= = {}|| **** || = {}|| **** || = {}||||||||| ||================= = = = = {}|| \\\ ||================== /// {}|| \\ || = // {} // // {}|| \\\ || ================== // {}|| \\ ||=================== ========= {}*/// 在I/O线程中注册某个回调函数,该函数可以跨线程调用void EventLoop::runInLoop(const Functor& cb){ if (isInLoopThread()) { // 如果是当前IO线程调用runInLoop,则同步调用cb cb(); } else { // 如果是其它线程调用runInLoop,则异步地将cb添加到队列,让EventLoop线程来执行这个cb()函数 queueInLoop(cb); }} // 把IO回调函数注册到pendingFunctors_中void EventLoop::queueInLoop(const Functor& cb){ { // 保护临界区 MutexLockGuard lock(mutex_); // 将任务添加到任务队列中 pendingFunctors_.push_back(cb); } // 1.--->调用queueInLoop的线程不是IO线程需要唤醒,以便IO线程及时处理任务 // 2.--->或者调用queueInLoop的线程是IO线程,并且此时正在调用pending functor,需要唤醒 /* 如果我们不唤醒,那么下一次poll就没法获取该cb的触发事件,这样就会使事件处理延迟了 ,请参照loop函数 */ // 3---->只有IO线程的事件回调中调用queueInLoop才不需要唤醒 ,因为handleEvent执行后, //下面接着执行doPendingFunctors if (!isInLoopThread() || callingPendingFunctors_) { wakeup(); }} TimerId EventLoop::runAt(const Timestamp& time, const TimerCallback& cb){ return timerQueue_->addTimer(cb, time, 0.0);} TimerId EventLoop::runAfter(double delay, const TimerCallback& cb){ Timestamp time(addTime(Timestamp::now(), delay)); return runAt(time, cb);} TimerId EventLoop::runEvery(double interval, const TimerCallback& cb){ Timestamp time(addTime(Timestamp::now(), interval)); return timerQueue_->addTimer(cb, time, interval);} void EventLoop::cancel(TimerId timerId){ return timerQueue_->cancel(timerId);} void EventLoop::updateChannel(Channel* channel){ assert(channel->ownerLoop() == this); assertInLoopThread(); poller_->updateChannel(channel);} void EventLoop::removeChannel(Channel* channel){ assert(channel->ownerLoop() == this); assertInLoopThread(); if (eventHandling_) { assert(currentActiveChannel_ == channel || std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end()); } poller_->removeChannel(channel);} void EventLoop::abortNotInLoopThread(){ LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this << " was created in threadId_ = " << threadId_ << ", current thread id = " << CurrentThread::tid();} /* 唤醒等待的线程*/void EventLoop::wakeup(){ uint64_t one = 1; //ssize_t n = sockets::write(wakeupFd_, &one, sizeof one); /*向wakeupFd_中写入数据*/ ssize_t n = ::write(wakeupFd_, &one, sizeof one); if (n != sizeof one) { LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8"; }} // IO事件回调函数void EventLoop::handleRead(){ uint64_t one = 1; //ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);--->>muduolib ssize_t n = ::read(wakeupFd_, &one, sizeof one); // 这是先这样设定 if (n != sizeof one) { LOG_ERROR << "EventLoop::handleRead() reads " << n << " bytes instead of 8"; }} /*IO线程的回调函数集 处理函数*/void EventLoop::doPendingFunctors(){ std::vector<Functor> functors; /*正在处于IO线程处理函数中*/ callingPendingFunctors_ = true; { // 加锁 MutexLockGuard lock(mutex_); //交换,顺带将pendingFunctors清空 functors.swap(pendingFunctors_); } for (size_t i = 0; i < functors.size(); ++i) { functors[i](); } /*清空IO线程回调处理函数中 的状态*/ callingPendingFunctors_ = false;} void EventLoop::printActiveChannels() const{ for (ChannelList::const_iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it) { const Channel* ch = *it; LOG_TRACE << "{" << ch->reventsToString() << "} "; }}
测试程序源代码
#include <muduo/net/EventLoop.h>//#include <muduo/net/EventLoopThread.h>//#include <muduo/base/Thread.h> #include <stdio.h> using namespace muduo;using namespace muduo::net; EventLoop* g_loop;int g_flag = 0; void run4(){ printf("run4(): pid = %d, flag = %d\n", getpid(), g_flag); g_loop->quit();} void run3(){ printf("run3(): pid = %d, flag = %d\n", getpid(), g_flag); g_loop->runAfter(3, run4); g_flag = 3;} void run2(){ printf("run2(): pid = %d, flag = %d\n", getpid(), g_flag); /*在IO线程中直接调用queueInLoop,并且if (!isInLoopThread() || callingPendingFunctors_)时, 不用唤醒(wakeup()) */ g_loop->queueInLoop(run3);} void run1(){ g_flag = 1; printf("run1(): pid = %d, flag = %d\n", getpid(), g_flag); /*在IO线程中,调用runInLoop时,不用经过queueInLoop()就可以直接调用Functors*/ g_loop->runInLoop(run2); g_flag = 2;} int main(){ printf("main(): pid = %d, flag = %d\n", getpid(), g_flag); EventLoop loop; g_loop = &loop; loop.runAfter(2, run1); loop.loop(); printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);}
程序输出
ubuntu@ubuntu-virtual-machine:~/29/jmuduo$ ../build/debug/bin/reactor_test05main(): pid = 28193, flag = 020131022 01:36:15.466526Z 28193 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:10420131022 01:36:15.467517Z 28193 TRACE EventLoop EventLoop created 0xBFEDB874 in thread 28193 - EventLoop.cc:6220131022 01:36:15.467586Z 28193 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:10420131022 01:36:15.467898Z 28193 TRACE loop EventLoop 0xBFEDB874 start looping - EventLoop.cc:9420131022 01:36:17.469365Z 28193 TRACE poll 1 events happended - EPollPoller.cc:6520131022 01:36:17.482824Z 28193 TRACE printActiveChannels {4: IN } - EventLoop.cc:25720131022 01:36:17.483170Z 28193 TRACE readTimerfd TimerQueue::handleRead() 1 at 1382405777.482909 - TimerQueue.cc:62run1(): pid = 28193, flag = 1run2(): pid = 28193, flag = 1run3(): pid = 28193, flag = 220131022 01:36:20.483953Z 28193 TRACE poll 1 events happended - EPollPoller.cc:6520131022 01:36:20.484074Z 28193 TRACE printActiveChannels {4: IN } - EventLoop.cc:25720131022 01:36:20.484119Z 28193 TRACE readTimerfd TimerQueue::handleRead() 1 at 1382405780.484109 - TimerQueue.cc:62run4(): pid = 28193, flag = 320131022 01:36:20.484229Z 28193 TRACE loop EventLoop 0xBFEDB874 stop looping - EventLoop.cc:119main(): pid = 28193, flag = 3ubuntu@ubuntu-virtual-machine:~/29/jmuduo$
- EventLoop再分析之IO线程(29)
- Netty源码细节之IO线程(EventLoop)
- muduo网络库学习之EventLoop(二):进程(线程)wait/notify 和 EventLoop::runInLoop
- Netty之基于EventLoop机制的高效线程模型
- Netty精粹之基于EventLoop机制的高效线程模型
- 【软考之用例图再分析】
- Vetx.x : EventLoop线程不要锁(Synchronized/ReentrantLock)
- netty5.0之EventLoop
- muduo网络库学习之EventLoop(七):TcpClient、Connector
- EventLoop
- 深入理解node.js异步编程(闭包,事件,内存回收,eventloop,io)
- 再分析“隐藏虚拟目录”
- “隐藏虚拟目录”再分析
- 单片机程序结构再分析
- ffmpeg tutorial01 再分析
- netback再分析
- ThreadLocal再分析
- arcgis soe再分析
- TCMalloc介绍
- Python中引入自定义路径下的用户自定义类
- Delphi视频教程:黑客基地delphi特训班
- NumPy学习笔记--入门篇
- ios中UIScrollview和Tableviewde 的嵌套使用
- EventLoop再分析之IO线程(29)
- sicily9161
- Spring MVC过滤器-字符集过滤器(CharacterEncodingFilter)
- C#远程连接mstscax组件和upnp组件
- 方格遍历问题
- EventLoopThread (30)
- XP系统ftp服务器的搭建
- Socket封装(31)
- ural 1963. Kite