(原创)拨开迷雾见月明-剖析asio中的proactor模式(二)

2024-03-26 10:50

本文主要是介绍(原创)拨开迷雾见月明-剖析asio中的proactor模式(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  在上一篇博文中我们提到异步请求是从上层开始,一层一层转发到最下面的服务层的对象win_iocp_socket_service,由它将请求转发到操作系统(调用windows api),操作系统处理完异步请求之后又是如何返回给应用程序的呢,这里是通过iocp(完成端口)来实现的。让我们先来简要的看看iocp的基本步骤:

  1. 创建IOCP对象;
  2. 创建io object对象;
  3. 将io object IOCP对象绑定;
    4.进行异步调用;
  4. 创建线程或者由线程池等待完成事件的到来;

  asio实际上也是按照这个步骤去做的,再回头看看上一节中的那个简单的例子:

asio::io_service io_service; 
tcp::socket socket(io_service); 
boost::asio::async_connect(socket, server_address, connect_handler); 
io_service.run(); 

  第一行中的io_service对象是asio的核心,它其实封装了iocp,创建一个io_service实际上就是创建了一个iocp对象win_iocp_io_service,因此后面所有的io object的创建都要引用这个io_service,目的是共用这个iocp对象。第二行创建了socket对象,它引用了第一行创建的iocp对象;第三行实际上是将异步请求层层转发到最下面的服务层win_iocp_socket_service对象,最终交给操作系统。通过它的名字就知道它与iocp相关,因为发起异步操作之前,它先要将io object对象与完成端口绑定,以便后面的完成事件会发到指定的完成端口。

  绑定io object和iocp对象的具体过程是这样的:async_connect内部会先调用base_xxx模板层的base_socket<tcp>的open方法,base_socket<tcp>又会调用服务层的服务对象stream_socket_service<tcp>的open方法,stream_socket_service<tcp>又调用最下面的服务对象win_iocp_socket_service的open方法,win_iocp_socket_service对象又委托io object对象引用的io_service对象(实际上是win_iocp_io_service)的do_open方法,在do_open方法中会调用register_handler方法,在该方法中会调用CreateIoCompletionPort将io object和iocp对象绑定起来。

  io object和iocp对象绑定之后,win_iocp_socket_service会调用操作系统的api,发起异步操作。

  再看第四行:io_service.run();

  io_service::run()又是委托win_iocp_io_service::run()来实现的,让我们来看看run的内部实现:

size_t win_iocp_io_service::run(boost::system::error_code& ec)
{if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0){stop();ec = boost::system::error_code();return 0;}win_iocp_thread_info this_thread;thread_call_stack::context ctx(this, this_thread);size_t n = 0;while (do_one(true, ec))if (n != (std::numeric_limits<size_t>::max)())++n;return n;
}

  run()首先检查是否有需要处理的操作,如果没有,函数退出;win_iocp_io_service使用outstanding_work_来记录当前需要处理的任务数。如果该数值不为0,则委托do_one函数继续处理。do_one()内部会调用GetQueuedCompletionStatus()函数,该函数会阻塞等待异步事件的完成,当异步事件完成时,就回调到应用层的完成事件处理函数,因为发起异步操作时已经将io object和完成端口绑定了,所以iocp能将异步完成事件回调到对应的应用层的完成处理函数中。

  至此,asio中一个异步操作的过程就完成了。在了解了这些内部实现细节之后,我们再来看看boost官网上给出的一个asio中proactor模式的一张图。

  这张图和上一篇博文中Proactor模式的图几乎是一样的,我们根据这张图再结合前面的分析,就能从细节中还原出asio中的Proactor模式了。下面我们来看看上图中的这些对象分别是asio中的哪些对象:

  • Initiator:对应用户调用asio的代码;
  • Asynchronous Operation Processor:异步操作处理器,他负责执行异步操作,并在操作完成后,把完成事件投放到完成事件队列上。stream_socket_service类就是一个这样的处理器,因为从tcp::socket发送的异步操作都是由其完成处理的,它最终是由底层的服务对象win_iocp_socket_service完成的,win_iocp_socket_service负责绑定io object和io_service对象和调用操作系统api发起异步操作。从高层的角度看,asio的stream_socket_service成为了Proactor中的异步操作处理器。
  • Asynchronous Operation:定义的一系列异步操作,对应到Windows平台,诸如AcceptEx,WSASend,WSARecv等函数。在asio中,这些函数封装在win_iocp_socket_service,resolver_service类中。[1]
  • Completion Handler:用户层完成事件处理器,由用户创建,一般是通过bind或者lambda表达式定义。
  • Completion Event Queue:完成事件队列,存储由异步操作处理器发送过来的完成事件,当异步事件多路分离器将其中一个事件取走之后,该事件从队列中删除;在Windows上,asio的完成事件队列由操作系统负责管理;
  • Asynchronous Event Demultiplexer:异步事件多路分离器,他的作用就是在完成事件队列上等待,一旦有事件到来,他就把该事件返回给调用者。在Windows上,这一功能也是由操作系统完成的,具体来说,是由GetQueuedCompletionStatus完成的,而该函数是由do_one()调用的,因此,从高层的角度来看,这个分离器,也是由io_service负责的。[2]
  • Proactor,前摄器,负责调度异步事件多路分离器去干活,并在异步操作完成时,调度所对应的Completion Handler。在asio中,这部分由io_service来做,具体Windows就是win_iocp_io_service。[3]

  从上面的分析可以看到,asoi中的Proactor模式已经很清晰了,io_service在asio中处于核心地位,不仅仅是对应了一个完成端口对象,还参与了Proactor模式中的异步事件处理和启动事件循环,调度异步事件多路分离器将异步事件回调到应用层。

  再来做一个小结:io object负责发起异步操作,发起异步操作的过程中,会委托stream_socket_service将异步操作转发到下面的服务层,最终转发到操作系统。io object创建时需要引用io_service,以便在后面绑定完成端口,同时还要提供完成事件处理函数,以便在异步操作完成后处理完成事件。io_service负责启动事件循环,等待异步事件的完成并将异步操作的结果回发到用户定义的完成事件处理函数中。


[1] [2] [3] http://blog.csdn.net/henan_lujun/article/details/8965044

如果你觉得这篇文章对你有用,可以点一下推荐,谢谢。

c++11 boost技术交流群:296561497,欢迎大家来交流技术。

这篇关于(原创)拨开迷雾见月明-剖析asio中的proactor模式(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/848259

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

Redis高可用-主从复制、哨兵模式与集群模式详解

《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M