(原创)拨开迷雾见月明-剖析asio中的proactor模式(二)
在上一篇博文中我们提到异步请求是从上层开始,一层一层转发到最下面的服务层的对象win_iocp_socket_service,由它将请求转发到操作系统(调用windows api),操作系统处理完异步请求之后又是如何返回给应用程序的呢,这里是通过iocp(完成端口)来实现的。让我们先来简要的看看iocp的基本步骤:
- 创建IOCP对象;
- 创建io object对象;
- 将io object IOCP对象绑定;
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_, ) == )
{
stop();
ec = boost::system::error_code();
return ;
} win_iocp_thread_info this_thread;
thread_call_stack::context ctx(this, this_thread); size_t n = ;
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
如果你觉得这篇文章对你有用,可以点一下推荐,谢谢。
(原创)拨开迷雾见月明-剖析asio中的proactor模式(二)的更多相关文章
- (原创)拨开迷雾见月明-剖析asio中的proactor模式(一)
使用asio之前要先对它的设计思想有所了解,了解设计思想将有助于我们理解和应用asio.asio是基于proactor模式的,asio的proactor模式隐藏于大量的细节当中,要找到它的踪迹,往往有 ...
- boost.asio源码剖析(四) ---- asio中的泛型概念(concepts)
* Protocol(通信协议) Protocol,是asio在网络编程方面最重要的一个concept.在第一章中的levelX类图中可以看到,所有提供网络相关功能的服务和I/O对象都需要Protoc ...
- 深入剖析Java中的装箱和拆箱
深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...
- 【翻译】Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解)
[翻译]Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解) . . .
- 从别人那淘的知识 深入剖析Java中的装箱和拆箱
(转载的海子的博文 海子:http://www.cnblogs.com/dolphin0520/) 深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来 ...
- 月半小夜曲下的畅想--DOCTYPE模式
月半小夜曲下的畅想--DOCTYPE模式 @(css3 box-sizing)[doctype声明|quirks模式|妙瞳] DOCTYPE文档类型标签,该标签是将特定的标准通用标记语言或者XML文档 ...
- 深入剖析Java中的自动装箱和拆箱过程
深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...
- [ 转载 ]学习笔记-深入剖析Java中的装箱和拆箱
深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...
- 【转】深入剖析Java中的装箱和拆箱
深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...
随机推荐
- 播布客视频PIT专用播放器MBOO2015
播布客视频,还是挺不错... 很多视频都是pit后缀的,需要用MBOO2015才可以打开... 00.MB2015软件 01.视频样例 02.download 链接: https://pan.baid ...
- Ubuntu18.04, WPS表格生成中文大写数字的script
=IF(ROUND(J9,2)<0,"无效数值",IF(ROUND(J9,2)=0,"零",IF(ROUND(J9,2)<1,"" ...
- 动态规划处理diff算法 Myers Diff (正向)
Eugene W. Myers 在他1986年发表于"Algorithmica"的论文"An O(ND) Difference Algorithm and Its Var ...
- Eclipse中10个最有用的快捷键组合(转)
Eclipse中10个最有用的快捷键组合 一个Eclipse骨灰级开发者总结了他认为最有用但又不太为人所知的快捷键组合.通过这些组合可以更加容易的浏览源代码,使得整体的开发效率和质量得到提升. ...
- 【总结】selenium webdriver 远程连接firefox和IE 环境搭建
参考链接:http://code.google.com/p/selenium/wiki/Grid2 本地环境为:win7,eclipse,jdk 1.7,本机ip为192.168.0.30 1.下载所 ...
- 是否只查看安全传送的网页内容? 去掉 IE弹出窗口
选择IE工具intemt选项,在选项卡里选择安全,然后在安全选项卡里点自定义级别,在设置里找到‘其他’这个分类,在次分类下找到‘显示混合内容’选择‘启用’然后保存退出就OK了,当然楼上几位说安全问题, ...
- Eclipse Java注释模板设置简介,更改字体大小
为类添加注释:此为我的模板设置注释模板的入口: Window->Preference->Java->Code Style->Code Template 然后展开Comments ...
- Lua初学
Lua很火啊,而且跟C,c++可以无缝结合,表示很给力,算是我的第三门语言吧,哈哈! 在官网上下载了源码了,和windows版的,表示编译器也很给力,直接可以用SciTE就可以写代码了. a = 1; ...
- RHEL/CentOS 7.x/6.x/5.x开启EPEL仓库
说明 原文链接 翻译:@adolphlwq 项目地址 这篇指南文章教你如何在 RHEL/CentOS 7.x/6.x/5.x 系统中开启EPEL仓库支持,以便你可以使用 yum 命令 安装额外的标准开 ...
- 【java】JVM的内存区域划分
学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的呢? 由于Java程序是交由JVM执行 ...