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

相关文章

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的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停