muduo库的EventLoopThread类剖析


关于EventLoopThread有以下几点:

1.任何一个线程,只要创建并运行了EventLoop,都称之为I/O线程。

2.I/O线程不一定是主线程。I/O线程中可能有I/O线程池和计算线程池。

3.muduo并发模型one loop per thread + threadpool

4.为了方便使用,就直接定义了一个I/O线程的类,就是EventLoopThread类,该类实际上就是对I/O线程的封装。

(1)EventLoopThread创建了一个线程。

(2)在该类线程函数中创建了一个EventLoop对象并调用EventLoop::loop


我们来看一下它的分析,后面会有示例:

头文件:

#include 
#include 
#include 

#include 

namespace muduo
{
namespace net
{

class EventLoop;

class EventLoopThread : boost::noncopyable
{
 public:
  typedef boost::function ThreadInitCallback;

  EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(),
                  const string& name = string());
  ~EventLoopThread();
  EventLoop* startLoop();  //启动成员thread_线程,该线程就成了I/O线程,内部调用thread_.start()

 private:
  void threadFunc();   //线程运行函数

  EventLoop* loop_;        //指向一个EventLoop对象,一个I/O线程有且只有一个EventLoop对象
  bool exiting_;
  Thread thread_;          //基于对象,包含了一个thread类对象
  MutexLock mutex_;
  Condition cond_;
  ThreadInitCallback callback_;   //回调函数在EventLoop::loop事件循环之前被调用
};

}
}

#endif  // MUDUO_NET_EVENTLOOPTHREAD_H

实现文件:

#include 

#include 

#include 

using namespace muduo;
using namespace muduo::net;


EventLoopThread::EventLoopThread(const ThreadInitCallback& cb,
                                 const string& name)
  : loop_(NULL),  //loop未启动为NULL
    exiting_(false),
    thread_(boost::bind(&EventLoopThread::threadFunc, this), name),  //绑定线程运行函数
    mutex_(),
    cond_(mutex_),
    callback_(cb)  //初始化回调函数
{
}

EventLoopThread::~EventLoopThread()
{
  exiting_ = true;
  if (loop_ != NULL) // not 100% race-free, eg. threadFunc could be running callback_.
  {
    // still a tiny chance to call destructed object, if threadFunc exits just now.
    // but when EventLoopThread destructs, usually programming is exiting anyway.
    loop_->quit();     //退出I/O线程,让I/O线程的loop循环退出,从而退出了I/O线程
    thread_.join();
  }
}

//启动EventLoopThread中的loop循环,内部实际调用thread_.start
EventLoop* EventLoopThread::startLoop()
{
  assert(!thread_.started());
  thread_.start();

  {
    MutexLockGuard lock(mutex_);
    while (loop_ == NULL)
    {
      cond_.wait();
    }
  }

  return loop_;
}

//该函数是EventLoopThread类的核心函数,作用是启动loop循环
//该函数和上面的startLoop函数并发执行,所以需要上锁和condition
void EventLoopThread::threadFunc()
{
  EventLoop loop;

  if (callback_)
  {
    callback_(&loop);  //构造函数传递进来的,线程启动执行回调函数
  }

  {
    MutexLockGuard lock(mutex_);
    loop_ = &loop;   //然后loop_指针指向了这个创建的栈上的对象,threadFunc退出之后,这个指针就失效了
    cond_.notify();   //该函数退出,意味着线程就推出了,EventLoopThread对象也就没有存在的价值了。但是muduo的EventLoopThread
    							//实现为自动销毁的。一般loop函数退出整个程序就退出了,因而不会有什么大的问题,
    							//因为muduo库的线程池就是启动时分配,并没有释放。所以线程结束一般来说就是整个程序结束了。
  }

  loop.loop();   //开始loop循环
  //assert(exiting_);
  loop_ = NULL;
}


这个类要注意的就是最后的这个两函数了,下面来分别说一下:

EventLoop* EventLoopThread::startLoop()
{
  assert(!thread_.started());
  thread_.start();

  {
    MutexLockGuard lock(mutex_);
    while (loop_ == NULL)
    {
      cond_.wait();
    }
  }

  return loop_;
}
这个函数是启动EventLoopThread类所包含的线程thread_,可以理解为启动worker线程,启动了之后该函数一直因condition不满足等待这里,该函数的作用是启动EventLoopThread中的线程去启动EventLoop,然后把EventLoop对象的指针返给调用者,由于启动线程,它和thread_的worker线程threadFunc是并发执行的,所以它需要等待loop产生成功才能返回loop的指针。loop是I/O线程最重要的循环不必多说,它是事件处理的核心。

那就看一下thread_的线程运行函数,知道thread_.start()之后,运行函数帮我们做了什么:

//该函数是EventLoopThread类的核心函数,作用是启动loop循环
//该函数和上面的startLoop函数并发执行,所以需要上锁和condition
void EventLoopThread::threadFunc()
{
  EventLoop loop;

  if (callback_)
  {
    callback_(&loop);  //构造函数传递进来的,线程启动执行回调函数
  }

  {
    MutexLockGuard lock(mutex_);
    loop_ = &loop;   //然后loop_指针指向了这个创建的栈上的对象,threadFunc退出之后,这个指针就失效了
    cond_.notify();   //该函数退出,意味着线程就推出了,EventLoopThread对象也就没有存在的价值了。但是muduo的EventLoopThread
    							//实现为自动销毁的。一般loop函数退出整个程序就退出了,因而不会有什么大的问题,
    							//因为muduo库的线程池就是启动时分配,并没有释放。所以线程结束一般来说就是整个程序结束了。
  }

  loop.loop();   //开始loop循环
  //assert(exiting_);
  loop_ = NULL;
}

这就是EventLoopThread类工作核心,如果EventLoopThread类构造时注册了回调函数,这个时候还可以先执行回调函数。

我们发现了一句重要的语句:

 EventLoop loop;

它在栈上声明了一个EventLoop对象loop,接下来又用loop_指针指向该对象,然后开始loop.loop()循环。当我们loop()结束时,把它赋为空。

我们发现loop()结束后,线程也就结束了,那么EventLoopThread对象已经没有存在的价值了。但是并没有实现立即销毁EventLoopThread。

这是因为该函数退出,意味着线程就退出了,EventLoopThread对象也就没有存在的价值了。但是muduo的EventLoopThread实现为自动销毁的。一般loop函数退出整个程序就退出了,因而不会有什么大的问题。因为muduo库的线程池就是启动时分配,并没有释放。所以线程结束一般来说就是整个程序结束了。


下面是一个测试例子:

#include 
#include 
#include 
using namespace muduo;
using namespace muduo::net;

void th_fn()
{
    printf("th_fn() : pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
}

int main()
{
    printf("main(): pid = %d, tid = %d\n",
                    getpid(), CurrentThread::tid());
    
    EventLoopThread loopThread;  //create a EventLoopThread object.
    /*EventLoop* EventLoopThread::EventLoopThread::startLoop()*/
    EventLoop* loop = loopThread.startLoop();
    //异步调用runInThread,即将runInThread添加到loop对象所在的I/O线程,让该I/O线程执行
    loop->runInLoop((th_fn));
    sleep(1);
    //runAfter内部也调用了runInLoop,所以这里也是异步调用
    loop->runAfter(2, th_fn);
    sleep(3);
    loop->quit();

    printf("exit main().\n");
    return 0;
}
编译选项要注意,muduo的静态链接库可能没有包含全部,所以加上编译具体的.cpp:

g++ -o main /usr/include/muduo/net/TimerQueue.cc /usr/include/muduo/net/EventLoop.cc event_loop_thread.cpp  -lmuduo_net -lmuduo_base -lpthread

输出:

muduo库的EventLoopThread类剖析_第1张图片

你可能感兴趣的:(Muduo源码剖析,muduo源码剖析)