muduo
是支持多线程的网络库,在muduo网络库学习(七)用于创建服务器的类TcpServer中也提及了TcpServer
中有一个事件驱动循环线程池,线程池中存在大量线程,每个线程运行一个EventLoop::loop
。
线程池的作用体现在
- 用户启动
TcpServer
服务器时创建大量子线程,每个子线程创建一个EventLoop
并开始执行EventLoop::loop
- 主线程的线程池保存着创建的这些线程和
EventLoop
- 当
Acceptor
接收到客户端的连接请求后返回TcpServer
,TcpServer
创建TcpConnection
用于管理tcp
连接 TcpServer
从事件驱动线程池中取出一个EventLoop
,并将这个EventLoop
传给TcpConnection
的构造函数TcpConnection
创建用于管理套接字的Channel
并注册到从线程池中取出的EventLoop
的Poller
中- 服务器继续监听
这个池子既是一个线程池,又是一个EventLoop
池,二者是等价的,一个EventLoop
对应一个线程
这种方式称为one loop per thread
即reactor + 线程池
线程池的定义比较简单,唯一复杂的地方是由主线程创建子线程,子线程创建EventLoop
并执行EventLoop::loop
,主线程返回创建的EventLoop
给线程池并保存起来,比较绕。
线程池EventLoopThreadPool
定义如下
class EventLoop;
class EventLoopThread;
class EventLoopThreadPool : noncopyable
{
public:
typedef std::function<void(EventLoop*)> ThreadInitCallback;
EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg);
~EventLoopThreadPool();
void setThreadNum(int numThreads) { numThreads_ = numThreads; }
/* 开启线程池,创建线程 */
void start(const ThreadInitCallback& cb = ThreadInitCallback());
// valid after calling start()
/// round-robin
/* 获取一个线程(事件驱动循环),通常在创建TcpConnection时调用 */
EventLoop* getNextLoop();
/// with the same hash code, it will always return the same EventLoop
EventLoop* getLoopForHash(size_t hashCode);
std::vector<EventLoop*> getAllLoops();
bool started() const
{ return started_; }
const string& name() const
{ return name_; }
private:
/* 主线程的事件驱动循环,TcpServer所在的事件驱动循环,创建TcpServer传入的EventLoop */
EventLoop* baseLoop_;
string name_;
bool started_;
/* 线程数 */
int numThreads_;
/* 标记下次应该取出哪个线程,采用round_robin */
int next_;
/* 线程池中所有的线程 */
std::vector<std::unique_ptr<EventLoopThread>> threads_;
/*
* 线程池中每个线程对应的事件驱动循环,从线程池取出线程实际上返回的是事件驱动循环
* 每个事件驱动循环运行在一个线程中
*/
std::vector<EventLoop*> loops_;
};
成员变量和函数没什么特别的,其中
baseLoop_
是主线程所在的事件驱动循环,即TcpServer
所在的那个主线程,这个事件驱动循环通常只负责监听客户端连接请求,即Acceptor
的Channel
。- 两个
vector
保存着所有子线程即每个子线程对应的EventLoop
。事件驱动循环线程被封装在EventLoopThread
中,EventLoopThread
中使用的Thread
才是真正的线程封装
线程池是由TcpServer
启动的,在TcpServer: