asio模型(io_context 和 thread的使用)

asio模型(io_context 和 thread的使用)

本文借鉴网址

Boost.asio有两种支持多线程的方式:

  • 多个io_context对应多线程(每个线程都有一个io_context,均调用各自的run方法
  • 一个io_context对应多线程(所有线程共用一个io_context,每个线程都调用全局的io_context.run 方法

每个线程一个 I/O Service

让我们先分析第一种方案:在多线程的场景下,每个线程都持有一个io_service (通常的做法是,让线程数和 CPU 核心数保持一致)。那么这种方案有什么特点呢?

  • 在多核的机器上,这种方案可以充分利用多个 CPU 核心。
  • 某个 socket 描述符并不会在多个线程之间共享,所以不需要引入同步机制。
  • 在 event handler 中不能执行阻塞的操作,否则将会阻塞掉io_service所在的线程。

下面实现了一个AsioIoContextPool的简单版线程池

#ifndef IOCONTEXTPOOL_H
#define IOCONTEXTPOOL_H

#include <boost/asio.hpp>
#include <thread>
#include <iostream>
#include <vector>

class AsioIoContextPool
{
public:
    using IOContext = boost::asio::io_context;
    using Work = boost::asio::io_context::work;
    using WorkPtr = std::unique_ptr<Work>;
    AsioIoContextPool(std::size_t size=std::thread::hardware_concurrency() );
    AsioIoContextPool(const AsioIoContextPool&)=delete ;
    AsioIoContextPool &operator=(const AsioIoContextPool &)=delete ;
    boost::asio::io_context &getIOCOntext();
    void stop();
private:
    std::vector<IOContext>      io_context_;
    std::vector<WorkPtr>        works_;
    std::vector<std::thread>    threads_;
    std::size_t                 nextIOContext_;
};

#endif // IOCONTEXTPOOL_H


#include "ioContextPool.h"

AsioIoContextPool::AsioIoContextPool(std::size_t size)
    :io_context_(size),
      works_(size),
      nextIOContext_(0)
{
    for(std::size_t i=0;i<size;i++)
        works_[i] = std::unique_ptr<Work>(new Work(io_context_[i]));

    for(std::size_t i=0;i<io_context_.size();i++)
        threads_.emplace_back([this,i](){
            io_context_[i].run();
        });
}

boost::asio::io_context &AsioIoContextPool::getIOCOntext()
{
    auto &service = io_context_[nextIOContext_++];
    if(nextIOContext_ == io_context_.size())
        nextIOContext_ = 0;
    return service;
}

void AsioIoContextPool::stop()
{
    for(auto& work: works_)
        work.reset();

    for(auto& t: threads_)
        t.join();
}

其中std::thread::hardware_concurrency()来获取硬件支持多少个并发线程

接下来用两个实例,来展示线程池的使用方法:

#include <iostream>
#include <ioContextPool.h>

using namespace std;

int main()
{
    std::mutex mtx;             // protect std::cout
    AsioIoContextPool pool;

    int j=1;
    //只有进行了pool.getIOContext就会分配一个线程
    boost::asio::steady_timer timer{pool.getIOCOntext(), std::chrono::seconds{4}};
    timer.async_wait([&mtx,&j] (const boost::system::error_code &ec)
                      {
                          std::lock_guard<std::mutex> lock(mtx); //锁,为了防止在访问j变量时,下一个线程访问改值
                          for(int i=0;i<10;i++)
                           {
                              cout<<j<<endl;
                              j++;
                          }
                          //这里执行到10 j=10
                      });
    boost::asio::steady_timer timer2{pool.getIOCOntext(), std::chrono::seconds{4}};
    timer2.async_wait([&mtx,&j] (const boost::system::error_code &ec)
                      {
                          std::lock_guard<std::mutex> lock(mtx);
                          for(int i=0;i<10;i++)
                          {
                             cout<<j<<endl;
                             j++;
                         }
                      });
    pool.stop();
}

执行结果:

执行

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`asio::io_context::strand::post` 和 `asio::steady_timer::expires_after` 都是 Boost.Asio 库中的功能。 `asio::io_context::strand::post` 可以将一个函数或者 Lambda 表达式提交到指定的 `strand` 上执行。`strand` 是 Boost.Asio 中用于处理多个线程访问同一个 `io_context` 的线程安全机制。 例如: ```cpp asio::io_context io_context; asio::io_context::strand strand(io_context); void handler1() { std::cout << "handler1: " << std::this_thread::get_id() << std::endl; } void handler2() { std::cout << "handler2: " << std::this_thread::get_id() << std::endl; } void post_handlers() { strand.post(handler1); strand.post(handler2); } std::thread t1(post_handlers); std::thread t2(post_handlers); io_context.run(); t1.join(); t2.join(); ``` 在上面的代码中,我们创建了一个 `io_context` 和一个 `strand`。然后我们创建了两个函数 `handler1` 和 `handler2`,并将它们都提交到了 `strand` 上执行。接下来,我们创建了两个线程分别执行 `post_handlers` 函数,这个函数会将 `handler1` 和 `handler2` 提交到 `strand` 上。最后我们启动了 `io_context` 的事件循环并等待两个线程执行完毕。 `asio::steady_timer::expires_after` 是一个定时器的功能,可以设置定时器在一定时间后超时。例如: ```cpp asio::io_context io_context; asio::steady_timer timer(io_context); void handler(const boost::system::error_code& error) { if (!error) { std::cout << "timer expired" << std::endl; } } void start_timer() { timer.expires_after(std::chrono::seconds(1)); timer.async_wait(handler); } start_timer(); io_context.run(); ``` 在上面的代码中,我们创建了一个 `io_context` 和一个 `steady_timer`。然后我们定义了一个回调函数 `handler`,当定时器超时时会被调用。接下来,我们创建了一个函数 `start_timer`,它会设置定时器超时时间为 1 秒,并异步等待定时器超时。最后我们启动了 `io_context` 的事件循环,当定时器超时时,`handler` 函数会被调用。 需要注意的是,`asio::steady_timer::expires_after` 的时间是相对时间,而不是绝对时间。如果需要设置绝对时间,可以使用 `asio::steady_timer::expires_at`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值