本文地址: https://blog.youkuaiyun.com/Mr_scott_j/article/details/111172724
EventLoopThreadPool也是派生自noncopyable,
用于创建IO线程池,用于把TcpConnection分派到某个具体的EventLoop线程上。EventLoopThreadPool是TcpServer的成员,生命周期由后者控制。
EventLoop应该比较熟悉,就是整个muduo库的核心,就是Reactor模式当中的Dispatcher(事件分发器),具体至EventLoop章节进行详细分析。
EventLoopThread启动了一个线程,并在其中运行EventLoop::loop(),实际上就是对实I/O线程的封装,亦不在此节详细说明。
muduo网络库的作者陈硕在书中提到,muduo的网络库的并发模型为one loop per thread+ threadpool模型。 每个线程最多有一个EventLoop,每个TcpConnection必须归某个EventLoop管理,所有的IO会转移到这个线程。TcpConnection所属的线程由其所属的EventLoop来决定。
为什么先选择EventLoopThreadPool类来进行剖析呢,因为其实这里就是多Reactor/多线程模式的实现(注:单Reactor时只需将numThreads_设置为0)。
EventLoopThreadPool接口如下:
其实只需关注start()、getNextLoop();
class EventLoopThreadPool : noncopyable
{
public:
typedef std::function<void(EventLoop*)> ThreadInitCallback;
EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg);
~EventLoopThreadPool();
void setThreadNum(int numThreads) { numThreads_ = numThreads; }//设置reactor数目
void start(const ThreadInitCallback& cb = ThreadInitCallback());//启动线程池
// valid after calling start()
/// round-robin
EventLoop* getNextLoop();//根据round-robin选择pool中的EventLoop
/// with the same hash code, it will always return the same EventLoop
EventLoop* getLoopForHash(size_t hashCode);
std::vector<EventLoop*> getAllLoops();
bool started() const;
const string& name() const;
private:
EventLoop* baseLoop_;//主线程的EventLoop
string name_;
bool started_;
int numThreads_;//线程数量 默认值为0
int next_;
std::vector<std::unique_ptr<EventLoopThread>> threads_;
std::vector<EventLoop*> loops_;
};
numThreads_决定了我们到底选择一种什么样的模式运行:numThreads_==1时,具有一个subReactor;numThreads_==0时,没有subReactor;numThreads_>1时,根据round-robin在线程池中选择一个Reactor。
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
......
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));
//启动每个EventLoopThread线程进入loop(),并且把返回的每个EventLoop指针压入到loops_
loops_.push_back(t->startLoop());
}
if (numThreads_ == 0 && cb)
{
//只有一个EventLoop,在这个EventLoop进入事件循环之前,调用cb
cb(baseLoop_);
}
}
getNextLoop()接口是给TcpServer使用的,每次有新TcpConnection建立,就会选择一个EventLoop;如果是单线程,则返回baseLoop_(TcpServer自己用的loop)。
至此,我们可以回顾之前在TcpConnection剖析中提到的将TcpServer::removeConnection()进行了一种拆分,为的就是保证removeConnection()操作移交回TcpServer自己的loop线程去,之后再将TcpConnection::connectDestroyed()移动回TcpConnection自己的ioLoop线程进行。也就是说,成员函数属于哪个对象,我们就让哪个函数来执行它。
可见,TcpConnection和TcpServer都是只处理单线程的情况,借助EventLoop::runInloop()并配合EventLoopThreadPool实现了多线程TcpServer。