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();
}
执行结果: