1 消息队列的任务类型 task_msg
1.1新建立连接的任务
该任务在main_thread接受accept成功之后进行发送,包含两个属性:type(NEW_CONN,本次连接的类型是新建连接任务还是一般任务), data(connfd)。
1.2一般任务
该任务是已经建立过的线程所需要执行的任务
struct task_msg
{
//两大类task:新建立连接任务+一般普通任务(主线程希望分发给每个线程的任务)
//
enum TASK_TYPE{
NEW_CONN, //新建连接的任务
NEW_TASK //一般的任务
};
TASK_TYPE type;
// 任务本身数据内容
union
{
int connfd;
};
// 任务1,tsak_msg的任务内容就是一个 刚刚创建好的connfd
// 任务2 具体的数据参数和具体的回调业务
};
2 task_queue
2.1属性
(1) evfd:当前消息队列的监听文件描述符
(2) _loop:让所绑定的thread中的event_loop来监控
(3) _queue队列
(4) _queue_tex:保护queue的一个互斥锁
int _evfd; //让某个线程进行监听的
event_loop *_loop; //目前被那个loop监听
std::queue<T> _queue;
pthread_mutex_t _queue_tex; //保护queue的互斥锁
2.2方法
(1)添加一个任务(由主线程或其他业务流程来调用)
void send(const T &task)
{
// 将task加入到queue中,激活_evfd
pthread_mutex_lock(&_queue_tex);
_queue.push(task);
// 激活_evfd可读事件,向_evfd写数据
unsigned int idle_num = 1;
int ret = write(_evfd, &idle_num, sizeof(unsigned long long));
if(ret==-1)
{
perror("evfd write error");
}
pthread_mutex_unlock(&_queue_tex);
}
(2)取出一个任务:当evfd被读事件激活,当前evfd所绑定的任务处理回调函数来调用该方法
// 从队列中取数据 将整个queue返回给上层
void recv(std::queue<T>& queue_msgs)
{
unsigned long long idle_num;
// 拿出queue数据
pthread_mutex_lock(&_queue_tex);
int ret = read(_evfd, &idle_num, sizeof(unsigned long long));
std::swap(queue_msgs, _queue);
pthread_mutex_unlock(&_queue_tex);
}
3 thread_pool
3.1属性
(1)thread_queue<task_msg>* *_queues:thread_queue的集合
(2)pthread_t* _tids:全部线程的ID
(3)int _thread_cnt:当前现成的个数
(4)int _index:获取线程的index索引
3.2方法
(1) thread_main()
根据线程个数(thread_cnt)初始化一个线程池之后,首先创建一个event_loop,用于监听当前线程所关联的connfd读写状态和对应的thread_queue的evfd读事件;之后将当前线程与thread_queue绑定;最后启动event_loop--->event_process()进行epollwait阻塞监听。
void *thread_main(void *args)
{
thread_queue<task_msg> *queue = (thread_queue<task_msg>*) args;
event_loop *loop = new event_loop();//每个线程都有loop
if(loop==NULL)
{
fprintf(stderr, "new event_loop error");
exit(1);
}
queue->set_loop(loop);
queue->set_callback(deal_task, queue);
// 启动loop监听
loop->event_process();
}
//设置当前thead_queue是被哪个事件触发event_loop监控
void set_loop(event_loop *loop)
{
this->_loop = loop;
}
//设置当前消息任务队列的 每个任务触发的回调业务
void set_callback(io_callback *cb, void *args)
{
if(_loop!=NULL)
{
_loop->add_io_envent(_evfd, cv=b, EPOLLIN, args);
}
//阻塞循环监听事件,并且处理epoll_wait,包含调用对应的触发回调函数
void event_loop::event_process()
{
io_event_map_it ev_it;
while(true)
{
int nfds = epoll_wait(_epfd, _fired_evs, MAXEVENTS, -1);
for(int i=0; i<nfds; i++)
{
//通过epoll出发的fd 在map中找到对应的io_event
ev_it = _io_evs.find(_fired_evs[i].data.fd);
//取出对应事件
io_event *ev = ev_it->second;
if(_fired_evs[i].enents & EPOLLIN)
{
//读事件,调用读回调函数
void *args = ev->rcb_args;
ev->read_callback(this, _fired_evs[i].data.fd, *args);
}
else if(_fired_evs[i].events & EPOLLOUT){
//写事件,调用写回调函数
void *args = ev->wcb_args;
ev->write_callback(this, _fired_evs[i].data.fd, *args);
else{
//删除
fprintf(stderr, "fd %d get error, delete from epoll", _fired_evs[i].data.fd);
this->del_io_event(_fired_evs[i].data.id);
}
}
}
}
}
(2) thread_pool()
依次创建thread_queue消息队列,让队列和对应线程进行绑定。
thread_pool::thread_pool(int thread_cnt)
{
_queues = NULL;
_thread_cnt = thread_cnt;
_index = 0;
// 创建thread_queue
_queues = new thread_queue<task_msg>*[thread_cnt]; // 开辟了cnt个thread_queue指针
_tids = new pthread_t[thread_cnt];
// 开辟线程
int ret;
for(int i = 0; i<thread_cnt; i++)
{
// 给每一个thread_queue开辟内存,创建对象
_queues[i] = new thread_queue<task_msg>();
//第i个线程绑定第i个thread_queue
ret = pthread_create(&_tids[i], NULL, thread_main, _queues[i]);
if(ret==-1)
{ perror("thread_pool create error");
exit(1);}
// 将线程设置为detach模型 线程分离模式
pthread_detach(_tids[i]);
}
}
4 将消息队列与连接池集成到reactor中
4.1 tcp_server中的新属性thread_pool* _thread_pool
tcp_server增加一个连接池属性,每次server在accept成功时,得到新的connfd,将connfd通过thread_queue发送给一个线程,让线程处理业务。
if(_thread_pool!=NULL)
{
// 开启多线程模式(将connfd交给线程去创建并监听)
thread_queue<task_msg>* queue = _thread_pool->get_thread();
task_msg task;
task.type = task_msg::NEW_CONN;
task.connfd = connfd;
queue->send(task);
}else{
// 创建一个新的tcp_conn连接对象
tcp_conn *conn = new tcp_conn(connfd, _loop);
if(conn==NULL)
{
fprintf(stderr, "new tcp_conn error\n");
exit(1);
}
break;
}
4.2 tcp_server构造函数中创建线程池
tcp_server::tcp_server(event_loop *loop, const char* ip, __uint128_t port)
{
//0.忽略一些信号 SIGHUB, SIGPIPE
if(singal(SIGHUP, SIG_IGN)==SIG_ERR){
fprintf(stderr, "signal ingore SIGHUB\n");
}
//1.创建socket
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(_sockfd==-1)
{
fprintf(stderr, "tcp::server socket()\n");
exit(1);
}
//2.初始化服务器地址
struct sockaddr_in server_addr;
ZeroMemory(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
inet_pton(ip, &server_addr.sin_addr);
server_addr.sin_port = htons(port);
//设置socket可以重复监听
int op=1;
if(setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&op, sizeof(op))<0){
fprintf(stderr, "set socketopt error\n");
exit(1);
}
//3.绑定端口
if(bind(_sockfd, (const struct sockaddr*)&server_addr, sizeof(server_addr))<0)
{
fprintf(stderr, "bind error\n");
exit(1);
}
//4.监听
if(listen(_sockfd, 500)==-1)
{
fprintf(stderr, "listen error\n");
exit(1);
}
// 5.将形参loop添加到tcp_server _loop中
_loop = loop;
//6 创建连接
_max_conns = MAX_CONNS;
conns = new tcp_conn*[_max_conns+3]; //3表示stdin, stdout, stderr
if(conns==NULL)
{
fprintf(stderr, "new conns error\n");
exit(1);
}
//7.创建线程池
int thread_cnt = 3;
if(thread_cnt>0){
_thread_pool = new thread_pool(thread_cnt);
if(_thread_pool==NULL)
{
fprintf(stderr, "tcp server new thread_pool error\n");
}
}