目前为止,涉及到的绝大多数操作都没有提及线程,EventLoop
,Poller
,Channel
,Acceptor
,TcpConnection
,这些对象的执行都是在单独线程完成,并没有设计多线程的创建销毁等。除了runInLoop
函数仅仅提及了线程安全性,发现原来其实是有多个线程在同时运行的,也发现某个对象可能会暴露给其他线程,这样不安全,为了解决不安全隐患,muduo
为每个EventLoop
设计了runInLoop
和queueInLoop
函数用来将本该在其他线程执行的线程不安全函数放到它所属线程执行,从而达到线程安全。
之所以线程的存在感比较低的原因在于,muduo
采用one loop per thread
的设计思想,即每个线程运行一个循环,这里的循环也就是事件驱动循环EventLoop
。所以,EventLoop
对象的loop
函数,包括间接引发的Poller
的poll
函数,Channel
的handleEvent
函数,以及TcpConnection
的handle*
函数都是在一个线程内完成的。而在整个muduo
体系中,有着多个这样的EventLoop
,每个EventLoop
都执行着loop
,poll
,handleEvent
,handle*
这些函数。这种设计模型也被成为Reactor + 线程池
而处于食物链顶层,控制着这些EventLoop
的,换句话说,保存着事件驱动循环线程池的,就是TcpServer
类。顾名思义,服务器类,用来创建一个高并发的服务器,内部便有一个线程池,线程池中有大量的线程,每个线程运行着一个事件驱动循环,即one loop per thread
。
另外,TcpServer
本身也是一个线程(主线程),也运行着一个EventLoop
,这个事件驱动循环仅仅用来监控客户端的连接请求,即Acceptor
对象的Channel
的可读事件。通常如果用户添加定时器任务的话,也会由这个EventLoop
监听
但是TcpServer
的这个EventLoop
不在线程池中,这一点要区分开,线程池中的线程只用来运行负责监控TcpConnection
的EventLoop
的
TcpServer的成员变量主要以EventLoop,Acceptor,TcpConnection, EventLoopThreadLoop为主,其它变量主要是各种用户提供的回调函数,大多都传给每一个TcpConnection对象
typedef std::map<string, TcpConnectionPtr> ConnectionMap;
/* TcpServer所在的主线程下运行的事件驱动循环,负责监听Acceptor的Channel */
EventLoop* loop_; // the acceptor loop
/* 服务器负责监听的本地ip和端口 */
const string ipPort_;
/* 服务器名字,创建时传入 */
const string name_;
/* Acceptor对象,负责监听客户端连接请求,运行在主线程的EventLoop中 */
std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor
/* 事件驱动线程池,池中每个线程运行一个EventLoop */
std::shared_ptr<EventLoopThreadPool> threadPool_;
/* 用户传入,有tcp连接到达或tcp连接关闭时调用,传给TcpConnection */
ConnectionCallback connectionCallback_;
/* 用户传入,对端发来消息时调用,传给TcpConnection */
MessageCallback messageCallback_;
/* 成功写入内核tcp缓冲区后调用,传给TcpConnection */
WriteCompleteCallback writeCompleteCallback_;
/* 线程池初始化完成后调用,传给EventLoopThreadPool,再传给每个EventLoopThread */
ThreadInitCallback threadInitCallback_;
AtomicInt