muduo源码分析--EventLoop 类的实现

首先看EventLoop的具体实现,因为继承了boost::noncopyable。所以这个类是不可拷贝的。
    从设计muduo的理念来看,one loop per thread顾名思义每个线程只能有一个EventLoop对象,因此EventLoop的构造函数就会检查当前线程是否已经创建了其他EventLoop对象,遇到错误就终止程序(LOG_FATAL).EventLoop的构造函数会记住本对象所属的线程(threadId_)。创建了EventLoop对象的线程是IO线程。其主要功能是运行时间循环EventLoop::loop()。EventLoop对象的生命周期通常和其所属的线程一样长,它不必是heap对象。因此在最后版本的EventLoop.cc中就会有一个__thread关键字标注的 t_loopInThisThread.
        同时要求,在当前线程中实例化loop对象,必须在当前线程中loop,在当前线程中updateChannel,在当前线程中removeChannel。
        也即是说在某线程中实例化EventLoop对象,这个线程就是IO线程,必须在IO线程中执行loop()操作,在当前IO线程中进行updateChannel,在当前线程中进行removeChannel
       

对SIGPIPE信号处理:
        SIGPIPE的默认行为是终止进程,在命令行程序中这是合理的,但是在网络编程中,这以为这如果对方断开连接而本地继续写入的话,会造成服务进程意外退出。
        假如服务进程繁忙,没有及时处理对方断开连接的事件,就有可能出现在连接断开之后继续发送数据的情况。这样的情况在这里是不能出现的
        解决办法很简单,在程序开始的时候就忽略SIGPIPE,可以用C++全局对象做到这一点。
   class InnoreSigPipe
{
        public:
            IgnoreSigPipe()
                {
                    ::signal(SIGPIPE,SIG_IGN);
                }
}
IgnoreSigPipe  initObj;



EventLoop中的runInLoop函数:
      这个函数是一个非常有用的函数,在他的IO线程内执行某个用户任务的回调,即EventLoop::runInLoop(const  Functor& cb),其中Functor是boost::functor<void()>.如果用户在当前IO线程调用这个函数,回调会同步进行;如果用户在其他线程调用runInLoop,cb会被加入队列,IO线程会被唤醒来调用这个Functor。
       有了这个功能,就能轻易地在线程间调配任务,比如讲某个函数调用移入其IO线程,这样可以在不用锁的情况下保证线程安全性!(这样,到目前为止EventLoop中存在两类情况,有些操作必须在一个线程中进行,比如实例化EventLoop的线程和执行loop(),执行updateChannel,removeChannel操作的线程都必须在一个线程进行,对于可以在别的线程执行的函数,又有一个方法,可以使用runInLoop函数执行。)
      比如说在主线程中实例化了一个EventLoop,将这个loop作为参数传递给一个线程,在这个线程内需要执行只能在创建loop的线程中执行的函数,就需要使用EventLoop中的runInLoop函数。
       和这个有关系的成员有pendingFunctors,只有pendingFunctors_暴漏给了其他线程,因为这个成员的修改是在queueInLoop函数中,这个函数是在runInLoop函数中调用的。因为别的线程想将回调在IO线程中执行,就必须使用runInLoop函数,在这个函数中使用了queueInLoop函数。将回调添加到pendingFunctors_中,然后再进行唤醒操作。
       唤醒是必须的两种情况:如果调用queueInLoop的线程不是IO线程;如果在IO线程调用queueInLoop,而此时正在调用pending functors,那么必须唤醒。也就是说,只有在IO线程的事件回调中调用queueInLoop()才无需wakeup()。
      在doPendingFunctors()中的处理也是非常巧妙的,首先将回调函数向量swap到局部变量functors中,减小了临界区的长度(意味着不会阻塞其他线程调用queueInLoop),另一方面也避免了死锁

你可能感兴趣的:(C++,网络编程)