io_service的作用

io_servie 实现了一个任务队列,这里的任务就是void(void)的函数。Io_servie最常用的两个接口是post和run,post向任务队列中投递任务,run是执行队列中的任务,直到全部执行完毕,并且run可以被N个线程调用。Io_service是完全线程安全的队列。

Io_servie的接口

提供的接口有run、run_one、poll、poll_one、stop、reset、dispatch、post,最常用的是run、post、stop

Io_servie 实现代码的基本类结构:

l  Io_servie是接口类,为实现跨平台,采用了策略模式,所有接口均有impl_type实现。根据平台不同impl_type分为

n  win_iocp_io_service Win版本的实现,这里主要分析Linux版本。

n  task_io_service 非win平台下的实现,其代码结构为:

u  detail/task_io_service_fwd.hpp 简单声明task_io_service名称

u  detail/task_io_service.hpp 声明task_io_service的方法和属性

u  detail/impl/task_io_service.ipp 具体实现文件

u  队列中的任务类型为opertioan,原型其实是typedef task_io_service_operation operation,其实现文件在detail/task_io_service_operation.hpp中,当队列中的任务被执行时,就是task_io_service_operation:: complete被调用的时候。

Io_servie::Post方法的实现

Post向队列中投递任务,然后激活空闲线程执行任务。其实现流程如下:

l  Post接收handler作为参数,实际上是个仿函数,通过此仿函数构造出completion_handler对象,completion_handler继承自operation。然后调用post_immediate_completion。

l  post_immediate_completion首先将outstanding_work_增加,然后调用post_deferred_completion。

l  post_deferred_completion首先加锁将任务入列,然后调用wake_one_thread_and_unlock

l  wake_one_thread_and_unlock尝试唤醒当前空闲的线程,其实现中特别之处在于,若没有空闲线程,但是有线程在执行task->run,即阻塞在epoll_wait上,那么先中断epoll_wait执行任务队列完成后再执行epoll_wait。

l  first_idle_thread_维护了所有当前空闲线程,实际上使用了Leader/Follower模式,每次唤醒时只唤醒空闲线程的第一个。

Io_servie::run方法的实现

Run方法执行队列中的所有任务,直到任务执行完毕。

l  run方法首先构造一个idle_thread_info,和first_idle_thread_类型相同,即通过first_idle_thread_将所有线程串联起来,它这个串联不是立即串联的,当该线程无任务可做是加入到first_idle_thread_的首部,有任务执行时,从first_idle_thread_中断开。这很正常,因为first_idle_thread_维护的是当前空闲线程。

l  加锁,循环执行do_one方法,直到do_one返回false

l  do_one每次执行一个任务。首先检查队列是否为空,若空将此线程追加到first_idle_thread_的首部,然后阻塞在条件变量上,直到被唤醒。

l  当被唤醒或是首次执行,若stopped_为true(即此时stop方法被调用了),返回0

l  队列非空,pop出一个任务,检查队列无任务那么简单的解锁,若仍有,调用wake_one_thread_and_unlock尝试唤醒其他空闲线程执行。然后执行该任务,返回1.

l  实际上在执行队列任务时有一个特别的判断if (o == &task_operation_),那么将会执行task_->run,task_变量类型为reactor,在linux平台实现为epoll_reactor,实现代码文件为detail/impl/epoll_reactor.ipp,run方法实际上执行的是epoll_wait,run阻塞在epoll_wait上等待事件到来,并且处理完事件后将需要回调的函数push到io_servie的任务队列中,虽然epoll_wait是阻塞的,但是它提供了interrupt函数,该interrupt是如何实现的呢,它向epoll_wait添加一个文件描述符,该文件描述符中有8个字节可读,这个文件描述符是专用于中断epoll_wait的,他被封装到select_interrupter中,select_interrupter实际上实现是eventfd_select_interrupter,在构造的时候通过pipe系统调用创建两个文件描述符,然后预先通过write_fd写8个字节,这8个字节一直保留。在添加到epoll_wait中采用EPOLLET水平触发,这样,只要select_interrupter的读文件描述符添加到epoll_wait中,立即中断epoll_wait。很是巧妙。!!!实际上就是因为有了这个reactor,它才叫io_servie,否则就是一个纯的任务队列了。

l  Run方法的原则是:

n  有任务立即执行任务,尽量使所有的线程一起执行任务

n  若没有任务,阻塞在epoll_wait上等待io事件

n  若有新任务到来,并且没有空闲线程,那么先中断epoll_wait,先执行任务

n  若队列中有任务,并且也需要epoll_wait监听事件,那么非阻塞调用epoll_wait(timeout字段设置为0),待任务执行完毕在阻塞在epoll_wait上。

n  几乎对线程的使用上达到了极致。

n  从这个函数中可以知道,在使用ASIO时,io_servie应该尽量多,这样可以使其epoll_wait占用的时间片最多,这样可以最大限度的响应IO事件,降低响应时延。但是每个io_servie::run占用一个线程,所以io_servie最佳应该和CPU的核数相同。

Io_servie::stop的实现

l  加锁,调用stop_all_threads

l  设置stopped_变量为true,遍历所有的空闲线程,依次唤醒

l  task_interrupted_设置为true,调用task_的interrupt方法

l  task_的类型为reactor,在run方法中已经做了分析

l

Boost::asio io_service 实现分析的更多相关文章

  1. 网络库crash以及boost asio strand dispath分析

    最近在做服务器的稳定性的相关测试,服务器的网络底层使用的是boost asio,然后自己做的二次封装以更好的满足需求. 服务器昨天晚上发现crash了一次,之前测试了将近半个多月,有一次是莫名的退出了 ...

  2. boost asio io_service学习笔记

    构造函数 构造函数的主要动作就是调用CreateIoCompletionPort创建了一个初始iocp. Dispatch和post的区别 Post一定是PostQueuedCompletionSta ...

  3. boost::asio::io_service::定时器任务队列

    使用io_service和定时器写的一个同步和异步方式的任务队列 #pragma once #include <string> #include <iostream> #inc ...

  4. 概念理解:boost::asio::io_service

    IO模型 io_service对象是asio框架中的调度器,所有异步io事件都是通过它来分发处理的(io对象的构造函数中都需要传入一个io_service对象). asio::io_service i ...

  5. boost::asio::io_service类

    大部分使用Boost.Asio编写的代码都会使用几个io_service的实例.io_service是这个库里面最重要的类:它负责和操作系统打交道,等待所有异步操作的结束,然后为每一个异步操作调用其完 ...

  6. boost.asio源码剖析(三) ---- 流程分析

    * 常见流程分析之一(Tcp异步连接) 我们用一个简单的demo分析Tcp异步连接的流程: #include <iostream> #include <boost/asio.hpp& ...

  7. boost.asio系列——io_service

    IO模型 io_service对象是asio框架中的调度器,所有异步io事件都是通过它来分发处理的(io对象的构造函数中都需要传入一个io_service对象). asio::io_service i ...

  8. boost asio 学习(一)io_service的基础

    原文  http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio/ 编译环境 b ...

  9. boost.asio源码剖析(五) ---- 泛型与面向对象的完美结合

    有人说C++是带类的C:有人说C++是面向对象编程语言:有人说C++是面向过程与面向对象结合的语言.类似的评论网上有很多,虽然正确,却片面,是断章取义之言. C++是实践的产物,C++并没有为了成为某 ...

随机推荐

  1. gpg-agent具体配置

    gpg(gnupg)工具使用很广泛,在前面一篇文章中就具体介绍:http://blog.csdn.net/dongtingzhizi/article/details/26362205,有一个问题值得关 ...

  2. splice()函数的使用方法

    splice()函数的使用方法,这是一个拗口的函数.用起来有点麻烦.图3所看到的是splice函数的功能.将一个列表插入到还有一个列表其中.list容器类定义了splice()函数的3个版本号: sp ...

  3. COM组件

    COM组件   COM component(COM组件)是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术.在COM构架下,人们可以开发出各种各样的功能专一的组件,然后 ...

  4. c# datagridviewcomboboxcell值无效的解决办法

    一直认为是数据库存储的数据和datagridviewcomboboxcell对不上导致,今天碰到两者对应上了,预览的时候还是提示错误, 查看了下网上其他大神的解决方法,是数据库字段类型有误,查看了下, ...

  5. C# 中datagridview行里面有三个cheeckbox,要控制成三选一。

    我之前有试过在cellendedit中处理,可以达成效果,当不符合用户打单的界面要求.该事件是在单元格编辑结束之后, 当用户选中两个checkbox,且焦点不移开时,界面上会出现有两个checkbox ...

  6. C# 中根据datetime的值来计算属于本年的第几周,类似delphi中的weekoftheyear功能

    /// <summary> /// 获得今天是今年的第几周 /// </summary> /// <param name="year">< ...

  7. 【SVN】is out of date

    右击项目(team->update 或者 team->freash/cleanup),再操作,提交就可以了.

  8. Xcode的Architectures、Valid Architectures和Build Active Architecture Only属性

    Architectures 这代表,在这个项目里你想要Xcode编译的目标设备列表. Valid Architectures 还不是太明确这个设置的意图,但是一般来说是不需要更改的,和Architec ...

  9. 欧拉函数K - Relatives

    欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n). 特殊性质:当n为奇数时,φ(2n)=φ(n), φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..( ...

  10. linux命令sysctl使用

    以前没有注意过这个命令,直到有次在单位安装greenplum的时候,在没有配置系统参数的情况下,出现了设备空间不足的报错信息. 当然,安装的不是我的本机,而是公用的服务器,编辑修改系统参数后,仍然出现 ...