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$


原创粉丝点击