类有哪些基本功能,实现了哪些实例,从构造函数看出来(看成员变量不是很直观,有些成员只有类的成员函数在使用);构造函数暴露的接口(入参)越少,类阅读起来思路越清晰,功能越内聚,特别是传入的其他类型的指针越少,与其他对象的耦合就越小
类保有其他类型的指针,最好不要像操作二级指针一样去操作这个指针,这就要求:设计这个指向的类时,它不要把自己的成员变量暴露出去
EPollPoller
只有一种形态,就是Poller::newDefaultPoller(this)
它只被EventLoop
所持有,也就是说EPollPoller
依附在一个EventLoop
身上,即EventLoop
与EPollPoller
一一对应
EventLoop
只在两个地方存在:
-
- 主线程中
-
EventLoopThread::threadFunc
函数栈上,并且之后调用了EventLoop::loop()
进入了"死"循环
-
EventLoop
依赖线程,依附在一个线程中执行,所以有个成员变量是const pid_t threadId
- 一,
EventLoop
构造函数与创建eventFd
的静态函数// 创建wakeupfd,用来notify唤醒subReactor处理新来的channel int createEventfd() { int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (evtfd < 0) { LOG_FATAL("eventfd error:%d \n", errno); } return evtfd; } // EventLoop 构造函数 EventLoop::EventLoop() : looping_(false) , quit_(false) , callingPendingFunctors_(false) , threadId_(CurrentThread::tid()) , poller_(Poller::newDefaultPoller(this)) , wakeupFd_(createEventfd()) , wakeupChannel_(new Channel(this, wakeupFd_)) { LOG_DEBUG("EventLoop created %p in thread %d \n", this, threadId_); if (t_loopInThisThread) { LOG_FATAL("Another EventLoop %p exists in this thread %d \n", t_loopInThisThread, threadId_); } else { t_loopInThisThread = this; } // 设置wakeupfd的事件类型以及发生事件后的回调操作 wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this)); // 每一个eventloop都将监听wakeupchannel的EPOLLIN读事件了 wakeupChannel_->enableReading(); }
- 二, 可以看出
EventLoop
:-
持有一个
EPollPoller
命名为poller_
EventLoop
与EPollPoller
一一对应关系 -
持有一个
::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)的返回值
即wakeupFd_
(也只有EventLoop
才持有eventFd
)
只有EventLoop
会持有它,是一一对应 -
持有一个用
wakeupFd_
构造的Channel
,把它命名成wakeupChannel_
EventLoop
与用wakeupFd_
构造的Channel
也是一一对应的 -
构造函数里的
wakeupChannel_->enableReading();
最终是执行下面(最终是::epoll_ctl(epollfd_, operation, fd, &event)
):
EventLoop
用其所保有的EPollPoller
执行下面代码,入参是在刚才构造出的wakeupChannel_(new Channel(this, wakeupFd_))
void EPollPoller::updateChannel(Channel *channel) { const int index = channel->index(); LOG_INFO("func=%s => fd=%d events=%d index=%d \n", __FUNCTION__, channel->fd(), channel->events(), index); if (index == kNew || index == kDeleted) { if (index == kNew) { int fd = channel->fd(); channels_[fd] = channel; } channel->set_index(kAdded); update(EPOLL_CTL_ADD, channel); } else // channel已经在poller上注册过了 { int fd = channel->fd(); if (channel->isNoneEvent()) { update(EPOLL_CTL_DEL, channel); channel->set_index(kDeleted); } else { update(EPOLL_CTL_MOD, channel); } } }
-
从上面代码亦可看出
EPollPoller
的channels_
首次挂载的是eventFd
using ChannelMap = std::unordered_map<int, Channel*>; ChannelMap channels_;
-
- 三, 什么时候会创建
EventLoop
?-
- 主线程中
-
- 下面函数中
// 下面这个方法,实在单独的新线程里面运行的 void EventLoopThread::threadFunc() { EventLoop loop; // 创建一个独立的eventloop,和上面的线程是一一对应的,one loop per thread if (callback_) { callback_(&loop); } { std::unique_lock<std::mutex> lock(mutex_); loop_ = &loop; cond_.notify_one(); } loop.loop(); // EventLoop loop => Poller.poll std::unique_lock<std::mutex> lock(mutex_); loop_ = nullptr; }
-
EPollPoller
在构造的时候首先创建并保有一份epoll
实例,以下是其构造函数
EPollPoller::EPollPoller(EventLoop *loop)
: Poller(loop)
, epollfd_(::epoll_create1(EPOLL_CLOEXEC))
, events_(kInitEventListSize) // vector<epoll_event>
{
if (epollfd_ < 0)
{
LOG_FATAL("epoll_create error:%d \n", errno);
}
}
EPollPoller
是项目上唯一一个对epoll_create1
及epoll_wait
和epoll_ctl
封装的类
Channel
只有三种形态
- 1,
new Channel(this, wakeupfd)
--> 区别eventFd
该对象被EventLoop
所持有 - 2,
new Channel(loop, sockfd)
--> 区别通信socket
该对象被TcpConnection
所持有 - 3,
Channel acceptChannel_;
--> 区别监听socket
该对象被Acceptor
所持有
(Acceptor
与TcpServer
一样,只有唯一一个实例,封装了监听socket
),以成员变量的形式持有Acceptor
在Acceptor
构造函数以参数初始化列表构造(用主线程中的EventLoop*
和监听socket
构造而成) - 4,
Channel
构造函数,从中能看出,构造时没做实质性工作,就是在接纳外部传进来的指针(EventLoop *
, 文件描述符)Channel::Channel(EventLoop *loop, int fd) : loop_(loop), fd_(fd), events_(0), revents_(0), index_(-1), tied_(false) { }
第一阶段分析到此为止,即执行到InetAddress addr(8000, "0.0.0.0");
此时只有:
- 一个
EventLoop
- 一个
Channel
(用eventFd
构造的,并将eventFd
挂载在了epoll
树上) - 一个
EPollPoller
依附在主线程(主函数)中的EventLoop
身上int main() { // 第一阶段 EventLoop loop; InetAddress addr(8000, "0.0.0.0"); // 第二阶段 EchoServer server(&loop, addr, "EchoServer-01"); // Acceptor non-blocking listenfd create bind // 第三阶段 server.start(); // listen loopthread listenfd => acceptChannel => mainLoop => loop.loop(); // 启动mainLoop的底层Poller return 0; }
- 第一阶段,主要看主线程中的
EventLoop
的工作,最终是将创建的第一个eventFd
通过epoll_ctl
挂载到epoll
树上
第二阶段分析从TcpServer
开始
EchoServer
构造函数EchoServer(EventLoop *loop, const InetAddress &addr, const std::string &name) : server_(loop, addr, name) , loop_(loop) { // 注册回调函数 server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, std::placeholders::_1) ); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); // 设置合适的loop线程数量 loopthread server_.setThreadNum(2); } void start() { server_.start(); }
TcpServer
对象保有主线程中的EventLoop*
TcpServer
构造函数static EventLoop* CheckLoopNotNull(EventLoop *loop) { if (loop == nullptr) { LOG_FATAL("%s:%s:%d mainLoop is null! \n", __FILE__, __FUNCTION__, __LINE__); } return loop; } // TcpServer 构造函数 TcpServer::TcpServer(EventLoop *loop, const InetAddress &listenAddr, const std::string &nameArg, Option option) : loop_(CheckLoopNotNull(loop)) , ipPort_(listenAddr.toIpPort()) , name_(nameArg) , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)) , threadPool_(new EventLoopThreadPool(loop, name_)) , connectionCallback_() , messageCallback_() , nextConnId_(1) , started_(0) { // 当有先用户连接时,会执行TcpServer::newConnection回调 acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2)); }
- 持有一份
Acceptor*
(用主线程中的EventLoop*
构造)
注意:Acceptor
与TcpServer
也是一一对应的(只有TcpServer
才持有Acceptor
)
整个程序中独一份的Acceptor
- 持有一份
EventLoopThreadPool*
,同样也是根据主线程中的EventLoop*
构造出的
EventLoopThreadPool
与TcpServer
一一对应
整个程序中独一份的EventLoopThreadPool
- 持有一份
Acceptor
构造函数与创建监听socket
的静态函数// 创建监听socket static int createNonblocking() { int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (sockfd < 0) { LOG_FATAL("%s:%s:%d listen socket create err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno); } return sockfd; } // 构造函数 Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport) : loop_(loop) , acceptSocket_(createNonblocking()) // socket , acceptChannel_(loop, acceptSocket_.fd()) , listenning_(false) { acceptSocket_.setReuseAddr(true); acceptSocket_.setReusePort(true); acceptSocket_.bindAddress(listenAddr); // bind // TcpServer::start() Acceptor.listen 有新用户的连接,要执行一个回调(connfd=》channel=》subloop) // baseLoop => acceptChannel_(listenfd) => acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this)); }
- 持有一份监听的
socket
即acceptSocket_(createNonblocking())
- 根据监听的
socket
(监听文件描述符)和主线程中的EventLoop*
生成一份Channel
并持有它,即acceptChannel_(loop, acceptSocket_.fd())
- 持有一份监听的
EventLoopThreadPool
构造函数EventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg) : baseLoop_(baseLoop) , name_(nameArg) , started_(false) , numThreads_(0) , next_(0) {}
- 构造函数里没做具体的事,保有主线程中的
EventLoop*
- 构造函数里没做具体的事,保有主线程中的
TcpConnection
构造函数
TcpConnection::TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CheckLoopNotNull(loop))
, name_(nameArg)
, state_(kConnecting)
, reading_(true)
, socket_(new Socket(sockfd))
, channel_(new Channel(loop, sockfd))
, localAddr_(localAddr)
, peerAddr_(peerAddr)
, highWaterMark_(64*1024*1024) // 64M
{
// 下面给channel设置相应的回调函数,poller给channel通知感兴趣的事件发生了,channel会回调相应的操作函数
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, std::placeholders::_1)
);
channel_->setWriteCallback(
std::bind(&TcpConnection::handleWrite, this)
);
channel_->setCloseCallback(
std::bind(&TcpConnection::handleClose, this)
);
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this)
);
LOG_INFO("TcpConnection::ctor[%s] at fd=%d\n", name_.c_str(), sockfd);
socket_->setKeepAlive(true);
}
第二阶段到此为止,主要是构造EchoServer server(&loop, addr, "EchoServer-01");
对象,也即TcpServer
对象
- 该对象(
TcpServer
)构造完成后,即创建了唯一的监听socket
,但并没有立即将其挂载到epoll
树上,TcpServer
的start()
方法才将监听socket
挂载到epoll
树上 - 构造过程:
TcpServer
与Acceptor
,TcpServer
与EventLoopThreadPool
都是一一对应关系EventLoop* loop_;
都是指主线程中的那个EventLoop
classDiagram
TcpServer o-- Acceptor
TcpServer o-- EventLoopThreadPool
Acceptor *-- Socket
Acceptor *-- Channel
class TcpServer {
- EventLoop* loop_;
// 运行在mainLoop,任务就是监听新连接事件
// Acceptor 与 TcpServer 一一对应
- std::unique_ptr<Acceptor> acceptor_;
// EventLoopThreadPool 与 TcpServer 一一对应
- std::shared_ptr<EventLoopThreadPool> threadPool_;
}
class Acceptor {
- EventLoop* loop_;
}
class EventLoopThreadPool {
- EventLoop* loop_;
// 管理根据线程数量创建的 EventLoopThread 对象
- std::vector<std::unique_ptr<EventLoopThread>> threads_;
// 管理容纳在 EventLoopThread::threadFunc 中的 EventLoop 对象
- std::vector<EventLoop*> loops_;
}
第三阶段分析main
函数中的server.start();
及loop.loop();
函数
- 有个前提条件
server_.setThreadNum(2);
设置sub_loop
的数量为2
server.start();
总览:
执行过程(将1执行两遍,因为设置的子loop
数为2
):-
EventLoop
(关联eventFd
) -->EventLoop::loop()
在子线程中执行loop
* 2
-
- 将监听
socket
挂载到epoll
树上
- 将监听
TcpServer::start()
函数// 开启服务器监听 loop.loop() void TcpServer::start() { if (started_++ == 0) // 防止一个TcpServer对象被start多次 { threadPool_->start(threadInitCallback_); // 启动底层的loop线程池 loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get())); } }
void EventLoopThreadPool::start(const ThreadInitCallback &cb)
下面函数中创建EventLoopThread
并用std::vector<std::unique_ptr<EventLoopThread>> threads_;
盛放void EventLoopThreadPool::start(const ThreadInitCallback &cb) { // 说明入参,实际回调 cb 未被实例化,可以看成是 nullptr started_ = true; for (int i = 0; i < numThreads_; ++i) { char buf[name_.size() + 32]; snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i); EventLoopThread *t = new EventLoopThread(cb, buf); threads_.push_back(std::unique_ptr<EventLoopThread>(t)); loops_.push_back(t->startLoop()); // 底层创建线程,绑定一个新的EventLoop,并返回该loop的地址 } // 整个服务端只有一个线程,运行着baseloop if (numThreads_ == 0 && cb) { cb(baseLoop_); } }
EventLoopThread::EventLoopThread
构造函数
EventLoopThread
对象在构造函数中先构造Thread
对象,名为thread_
(为和上面相区分,摘录它作为成员变量的定义Thread thread_;
)
注意:实际的多线程函数运行时的实例是EventLoopThread::threadFunc
后面会给出函数定义
实际的多线程运行的代码实体,注意它里面创建了EventLoop loop对象// 说明:经过观察,入参的回调对象未被实现,可以理解成 cb = nullptr EventLoopThread::EventLoopThread(const ThreadInitCallback &cb, const std::string &name) : loop_(nullptr) , exiting_(false) , thread_(std::bind(&EventLoopThread::threadFunc, this), name) , mutex_() , cond_() , callback_(cb) { }
至此,这个局部对象又执行loop.loop()
函数,即进入"死"循环
非常合理!在子线程中执行"死"循环,(要等将子线程start()
,即上面代码执行t->startLoop()
)// 下面这个方法,是在单独的新线程里面运行的 void EventLoopThread::threadFunc() { EventLoop loop; // 创建一个独立的eventloop,和上面的线程是一一对应的,one loop per thread if (callback_) { callback_(&loop); } { std::unique_lock<std::mutex> lock(mutex_); loop_ = &loop; cond_.notify_one(); } loop.loop(); // EventLoop loop => Poller.poll std::unique_lock<std::mutex> lock(mutex_); loop_ = nullptr; }
Thread
封装了std::thread
class Thread : noncopyable { public: using ThreadFunc = std::function<void()>; explicit Thread(ThreadFunc, const std::string &name = std::string());
t->startLoop()
定义,注意看里面有thread_.start();
在里面启动多线程EventLoop* EventLoopThread::startLoop() { thread_.start(); // 启动底层的新线程 EventLoop *loop = nullptr; { std::unique_lock<std::mutex> lock(mutex_); while ( loop_ == nullptr ) { cond_.wait(lock); } loop = loop_; } return loop; }
thread_.start();
定义 :void Thread::start() // 一个Thread对象,记录的就是一个新线程的详细信息 { started_ = true; sem_t sem; sem_init(&sem, false, 0); // 开启线程 thread_ = std::shared_ptr<std::thread>(new std::thread([&](){ // 获取线程的tid值 tid_ = CurrentThread::tid(); sem_post(&sem); // 开启一个新线程,专门执行该线程函数 func_(); })); // 这里必须等待获取上面新创建的线程的tid值 sem_wait(&sem); }
void Acceptor::listen()
void Acceptor::listen() { listenning_ = true; acceptSocket_.listen(); // listen acceptChannel_.enableReading(); // acceptChannel_ => Poller }
-
再看
EventLoop
是个通信类,它持有的EpollPoller
和Channel(eventFd构造的)
通过EventLoop
通信;特别是Channel
通过EventLoop*
拿到EpollPoller*
,并将自己Channel*
传递给它
Q:有个EventLoop
对象代表了什么?
- A:一个
EventLoop
对象表示:
持有EpollPoller
对象
持有eventFd
(用作异步通知的fd
)
持有Channel(this, wakeupFd_)
以及继续完善Channel
(在EventLoop
构造函数中做的工作)- 完善
Channel::ReadEventCallback readCallback_;
变量
即std::bind(&EventLoop::handleRead, this, std::placeholders::_1)
把回调函数(readCallback_
对象)设置成这个样子 - 将
wakeupFd_
挂载到由EventLoop
所管理的EpollPoller
的epoll
树上
- 完善
简单看,只有EventLoop loop;
和loop.loop();
会发生什么?
- A:上面已经分析了
EventLoop loop;
对象,下面贴出EventLoop::loop
代码
EventLoop::loop
// 开启事件循环 void EventLoop::loop() { looping_ = true; quit_ = false; LOG_INFO("EventLoop %p start looping \n", this); while(!quit_) { activeChannels_.clear(); // 监听两类fd 一种是client的fd,一种wakeupfd pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); for (Channel *channel : activeChannels_) { // Poller监听哪些channel发生事件了,然后上报给EventLoop,通知channel处理相应的事件 channel->handleEvent(pollReturnTime_); } // 执行当前EventLoop事件循环需要处理的回调操作 /** * IO线程 mainLoop accept fd《=channel subloop * mainLoop 事先注册一个回调cb(需要subloop来执行) wakeup subloop后,执行下面的方法,执行之前mainloop注册的cb操作 */ doPendingFunctors(); } LOG_INFO("EventLoop %p stop looping. \n", this); looping_ = false; }
__thread EventLoop *t_loopInThisThread = nullptr;
很趣,作用是:防止一个线程创建多个EventLoop thread_local
// 构造函数中这么用,用它作判断
if (t_loopInThisThread)
{
LOG_FATAL("Another EventLoop %p exists in this thread %d \n", t_loopInThisThread, threadId_);
}
else
{
t_loopInThisThread = this;
}
// 析构时
t_loopInThisThread = nullptr;