(原创)拨开迷雾见月明-剖析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

相关文章

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

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序