boost::io_service解读

简介: boost::io_service解读 asio是boost提供的一个c++异步编程模型库,其核心类io_service,在多线程编程里面提供了任务队列和任务分发功能,在socket、io编程里主要作为一个事件驱动器(完成端口、select、poll、epoll等)。

boost::io_service解读

asio是boost提供的一个c++异步编程模型库,其核心类io_service,在多线程编程里面提供了任务队列和任务分发功能,在socket、io编程里主要作为一个事件驱动器(完成端口、select、poll、epoll等)。

队列模型

每个io_service都一个公有任务队列,和多个私有任务队列,公有队列由各个线程共享,私有队列则是每个线程独享一个。

队列模型

io_service的任务执行流程大致如下:

  1. 调用run方法,进入主loop;
  2. 判断公有队列是否为空,不为空则取出任务并执行,当任务数大于1时同时唤醒其他空闲线程;
  3. 任务执行结束,把各个线程的私有队里面的任务移动到公有任务队列里面;
  4. 触发reactor,linux下面一般是epoll,当有事件时,把相应的事件的任务放到私有队列里。
  5. 当队列为空时,把当前线程加到空闲线程队列里面,同时进入wait状态,等待其他线程的唤醒(task_operation)。
  6. 当用户调用post时,任务是直接投递到公有队列op_queue里面。

线程池模型

常用的线程池模型有两种:

一种是多个线程共享一个任务队列,用户把任务投递到该任务队列中,其他线程竞争从该队列中获取任务执行。结合boost::thread,在多个线程里面调用run方法,即可实现该线程池:

using namespace boost;
using namespace boost::asio;
io_service ios;
int thread_num = 10;
thread *t[thread_num] = {0};
// 创建线程池
for(int i = 0; i < thread_num; ++i)
{
    t[i] = new thread(bind(&io_service::run, &ios));
}

// 向任务队列中投递任务,该任务将随机由一个调用run方法的线程执行
ios.post(func);

// 等待线程退出
for(int i = 0; i < thread_num; ++i)
{
    t[i]->join();
}

很容易看出来,这种线程池的瓶颈在于一个任务队列,多个线程竞争取任务,在大并发的程序里面容易导致性能下降。

另一种是每个线程各自维护一个任务队列,用户可以选择随机或者轮训地投递任务到其中一个任务队列里面,该任务队列中的任务只由其所在的线程才能消费。这种线程池在boost的example里面也有相应的实现(io_service_pool),基本方法是创建多个io_service对象,每个对应一个thread,代码如下:

using namespace boost;
using namespace boost::asio;
int thread_num = 10;
io_service ios[thread_num];
thread *t[thread_num] = {0};
// 创建线程池
for(int i = 0; i < thread_num; ++i)
{
    t[i] = new thread(bind(&io_service::run, &ios[i]));
}

// 轮训投递任务
for(int i = 0; i < thread_num; ++i)
{
    ios[i].post(func);
}   

// 等待线程退出
for(int i = 0; i < thread_num; ++i)
{
    t[i]->join();
}

源码剖析

以下是基于linux环境作的类图,因为在windows上,有些类有不同的定义。

uml

io_service定义了主要的接口,在linux下的实现是task_io_service。

task_io_service主要定义了三个东西:

  1. 一个reactor,reactor就是完成端口、select、poll、epoll之类的事件驱动器;
  2. 一个公共的任务队列op_queue,用来存放用户post的任务,和reactor返回的任务;
  3. 线程相关变量。io_service本身不会创建任何线程,但它会保存一些线程调用信息,如线程私有队列等。

此外,task_io_service还维护了一个空闲线程列表,在有多余任务到来时,唤醒其中一个空闲线程。在常见的linux单任务队列的线程池里面,用一个condition变量来唤醒线程,在多核系统里面,一次pthread_cond_signal调用,会唤起处于wait状态的一个或多个线程,尽管只有一个任务,如果采用空闲线程的方法,有任务时只唤醒一个空闲线程,可以减少很多不必要唤醒。

thread_info_base类维护了一个简单的内存池,只有一块内存,仅在连续的申请释放内存情况下,可以减少内存开辟的开销。

io_service::work的作用:io_service::run在任务执行完毕时会立刻返回,这个并不是编写常驻服务的程序所想要的,boost给的解决方案是定义一个work变量,乍看很神奇,这个跟io_server似乎没有任何关系的变量竟然能控制run的行为,打开源码一看,work的实现简单的出奇,它仅仅是在构造函数里调用io_service的worked_started()方法,使其待处理任务数(++outstanding_work_)大于0,如此,io_service::run就认为一直有任务待处理而不返回了。

谢谢。

目录
相关文章
|
9月前
|
人工智能 自然语言处理 前端开发
2026年AI生成网站怎么做?
本文探讨网站开发抽象层级的持续上移:从手写代码→模板/CMS→组件化→代码生成,再到2026年兴起的AI结构级生成。其核心是将开发起点从“写代码”转向“描述系统结构”,显著降低结构搭建成本(可压缩30%–60%),推动开发者角色向结构表达与架构决策升级。
|
存储 编解码 算法
超级好用的C++实用库之Base64编解码
超级好用的C++实用库之Base64编解码
1340 2
|
机器学习/深度学习 自然语言处理 算法
文心4.5系列模型,正式开源!
6月30日,百度文心大模型4.5正式开源,魔搭社区在开源首日快速接入文心大模型,提供真正可用、好用、可落地的大模型解决方案,现已面向广大企业、开发者下载体验!
924 1
|
存储 搜索推荐 JavaScript
【毕业设计之java系列】基于springboot学生社团信息管理系统
【毕业设计之java系列】基于springboot学生社团信息管理系统
1298 0
|
并行计算 异构计算
卸载原有的cuda,更新cuda
本文提供了一个更新CUDA版本的详细指南,包括如何查看当前CUDA版本、检查可安装的CUDA版本、卸载旧版本CUDA以及安装新版本的CUDA。
18112 3
卸载原有的cuda,更新cuda
|
存储 Linux 编译器
cmake的单目录和多目录的使用(Linux和Windows)
本文介绍了在Windows和Linux平台上使用CMake构建单目录和多目录项目的步骤,包括如何配置CMakeLists.txt文件以及如何生成和使用可执行文件、库文件。
1030 2
|
C++
C++11日志之spdlog
C++11日志之spdlog
569 1
|
机器学习/深度学习 存储 人工智能
GEE Colab——初学者福音快速入门 Google Colab(Colaboratory)
GEE Colab——初学者福音快速入门 Google Colab(Colaboratory)
1142 3
|
移动开发 索引
技术笔记:Word域功能详细介绍(一)
技术笔记:Word域功能详细介绍(一)
|
Java 程序员 C#
程序员必知:UML关联聚合组合关系
程序员必知:UML关联聚合组合关系
722 0

热门文章

最新文章