muduo
In EventLoopThreadPool
the constructor of the class , the member is numThreads_
set to 0, which means that the master-slave Reactor mode is not turned on by default , that is, the single Reactor mode.
In single Reactor
mode, it Reactor
is responsible for monitoring the arrival of new connections and the read and write of the socket.
Pass before the call void TcpServer::start()
, call EventLoopThreadPool::setThreadNum()
. You can turn on the master-slave Reactor mode .
TcpServe
The structure of class r is as follows:
class TcpServer : noncopyable
{
public:
...
private:
EventLoop* loop_; // the acceptor loop
...
std::shared_ptr<EventLoopThreadPool> threadPool_;
...
};
TcpServer
When constructing, the constructed EventLoop object is passed in and assigned to the member loop_
, which loop_
runs in the main thread.
Saying it 个EventLoop
is the main the Reactor , only responsible for monitoring new connection requests .
TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option)
: loop_(CHECK_NOTNULL(loop)), // 外部直接传入
ipPort_(listenAddr.toIpPort()),
name_(nameArg),
acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)), // 将loop_作为主reactor使用
threadPool_(new EventLoopThreadPool(loop, name_)),
connectionCallback_(defaultConnectionCallback),
messageCallback_(defaultMessageCallback),
nextConnId_(1)
{
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, _1, _2));
}
When the server starts, it will be called TcpServer::start()
, and it will be called again EventLoopThreadPool::start(const ThreadInitCallback& cb)
to initialize and run the child threads and save them in the infinite event loop TcpServer::threadPool_
running in these child threads EventLoop
. Call these runs EventLoopThread
are EventLoop
as from the Reactor .
void TcpServer::start()
{
if (started_.getAndSet(1) == 0)
{
threadPool_->start(threadInitCallback_);
assert(!acceptor_->listenning());
loop_->runInLoop(
std::bind(&Acceptor::listen, get_pointer(acceptor_)));
}
}
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
assert(!started_);
baseLoop_->assertInLoopThread();
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());
}
if (numThreads_ == 0 && cb)
{
cb(baseLoop_);
}
}
For connected sockets, monitoring their events is the responsibility of the slave Reactor, that is, the EventLoop running in the child thread .
When a slave Reactor is needed, it needs to be called EventLoopThreadPool->getNextLoop()
;
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
EventLoop* ioLoop = threadPool_->getNextLoop(); // 这里获取一个从reactor
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd));
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
...;
}
EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
assert(started_);
EventLoop* loop = baseLoop_;
if (!loops_.empty())
{
// round-robin
loop = loops_[next_];
++next_;
if (implicit_cast<size_t>(next_) >= loops_.size())
{
next_ = 0;
}
}
return loop;
}