基于C++的Linux高性能服务器6

7 篇文章 0 订阅

鸽了挺长时间,其实最后一部分早就写完了,但是最近实验有点多,一直在忙实验。

这次新增了很多东西,首先是Buffer类,用来处理接收或者要发送的信息,不再像以前一样收到一次发一次,而是把收到或者要发送的消息汇总起来,等到输入结束的时候在一起发送,其实用起来跟之前也什么区别,但是封装之后使用更方便一点。Buffer类管理一个string字符串,比较简单,不再赘述。

还增加了线程池,原先服务器所有的事件都是由Eventloop管理的,单线程处理,加入同时处理1000个1s的事件,则会阻塞相当长时间。在前面提到的Reactor模式中讲过,Reactor应该只负责分发事件,由处理事件线程来处理时间。这里添加了一个简单的线程池,线程的数量取决于CPU,我在Linux虚拟机上看是只有八个,可能是虚拟机核数分少了,事件活跃时将时间添加到任务队列,然后取出由各线程完成。

需要注意的时多线程操作需要配合互斥锁来使用,避免同时对某一资源读写时造成不可预见的后果,同时采用std::condition_variable来通知对应线程处理避免CPU轮询任务队列造成资源浪费。代码如下:

template<class F, class... Args>
auto ThreadPool::add(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using returnType = typename std::result_of<F(Args...)>::type;

    auto task = std::make_shared<std::packaged_task<returnType()>> (
        std::bind(std::forward<F>(f), std::forward<Args>(args)...)
    );

    std::future<returnType> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(tasksMtx);

        if (stop) {
            throw std::runtime_error("enqueue on stopped Thread pool");
        }

        tasks.emplace([task](){ (*task)(); });
    }
    cv.notify_one();
    return res;
}

看起来相当的恐怖,采用了模板和大量的c++新特性,returnType为返回值类型,task是一个指向bind绑定的函数对象的返回值类型的指针,packaged_task用于封装bind的绑定对象,并能将其返回值传递给future对象,供后面res的get_future()调用。然后是大括号内的互斥锁作用域,保证该对象在同一时刻只有一个线程访问,也就是将其添加到tasks任务队列中,之所以再开一个作用域是因为当退出作用域后锁会自动释放。
最后调用notify_one()通知线程池获取任务在处理。

然后将服务器由单Reacor改为主从Reactor多线程模式,
该模式只有一个mainReactor,有很多个subReactor。
服务器管理一个线程池,每一个subReactor由一个线程来负责Connection上的事件循环,事件执行也在这个线程中完成。
mainReactor只负责Acceptor建立新连接,然后将这个连接分配给一个subReactor。

在构造服务器时

//Acceptor由且只由mainReactor负责
Server::Server(pEventLoop _loop) : mainReactor(_loop), acceptor(new Acceptor(mainReactor)), thPool(new ThreadPool()) {
    std::function<void(pSocket)> cb = std::bind(&Server::newConnection, this, std::placeholders::_1);
    acceptor->setNewConnectionCB(cb);

    int size = std::thread::hardware_concurrency();//线程数量,也是subReactor数量
    for (int i = 0; i < size; i++) {
        subReactors.emplace_back(pEventLoop(new EventLoop()));  //每一个线程是一个EventLoop
    }

    for (int i = 0; i < size; i++) {
        std::function<void()> subLoop = std::bind(&EventLoop::loop, subReactors[i]);
        thPool->add(std::move(subLoop)); //开启所有线程的事件循环
    }
}

在新连接到来时,我们需要将这个连接的socket描述符添加到一个subReactor中:

int random = sock->getFd() % subReactors.size();    //调度策略:全随机
Connection *conn = new Connection(subReactors[random], sock);   //分配给一个subReactor
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值