Day6:Lar V0.8 消息队列与线程池

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");
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值