Boost.ASIO源码:deadline_timer源码级解析(三)—— 从源码解释io_service::run()到底发生了什么

前前后后run这个函数来来往往反反复复看了不知道多少遍,对它的逻辑始终没弄明白,直到最近研究deadline_timer才恍然大悟理清了前面的一些逻辑,在此顺便总结一下,也算是填了前面几个博客一直没讲明白的一些点。(因为那时我还没完全看懂,故只能留坑了。。)
我前面所有的博客都算是这篇博客的铺垫,本文中也多次引用了我以前写的博客,需要的时候建议还是参考下,不然可能有点难以理解。

前文回顾

前面两个博客一个讲了deadline_timer的调用逻辑,一个讲了epoll_reactor的触发逻辑。其实最重要的真正处理逻辑一直没讲,而这个处理逻辑,便是在io_service::run()中。
前面两篇文章,第一篇是讲到epoll_reactor::schedule_timer就没有扩展下去了,第二篇在第一篇的基础上进行扩展,但讲到epoll_reactor::run()也没有扩展下去了。在这里我还是建议一下至少先看前面两篇文章,虽然直接看这篇文章也能对io_service::run()有所了解,但是从最外层调用开始看能对整个代码逻辑有更宏观的理解。

io_service::run的背景相关

这些其实大部分前面的博客都说过了,但在这里还是总结说明一下。
io_service是一个别名,它的本名叫io_context,而io_context继承自execution_context,代表一个上下文环境,这个execution_context持有一个service_registry对象,service_registry维护所有的服务,每种服务只会在service_registry中存在一个对象,这个主要通过use_service这个函数实现。而服务说直白点,就是调用ASIO的那些接口类所调用的逻辑处理函数集合体(当然服务里还会持有相关数据),所有的服务都继承自execution_context::service,这个基类规定,所有的服务都要持有它的运行上下文对象,即execution_context(具体点方便理解,这里就把execution_context直接看作io_service就行了)。而io_context实际上也是不干实事的,io_context有个成员叫impl_,这个成员可以理解为io_context的具体逻辑实现类,io_context几乎所有函数都是调用这个impl_的接口函数。在这里,这个impl_就是我们前面多次提到的scheduler
所以所谓的io_service.run,它的真实逻辑在scheduler.run中。

从构造scheduler到scheduler::run()到底发生了什么

这里还提到了构造scheduler,scheduler在构造时实际上还做了一些很不明显的工作,如果忽略的话scheduler::run()的逻辑就讲不通了(这也是前面我一直没看懂的原因)。
首先看到一个最基本的deadline_timer用法,后面会以这个例子为例来讲解run的逻辑:

void Print(const boost::system::error_code &ec);
boost::asio::io_service io;  
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  
t.async_wait(Print);  
io.run()

从第2行构造io_service看起,此时当然实际上执行的是io_context的空参构造函数,其中会以默认的方式构造scheduler:

io_context::io_context()
  : impl_(add_impl(new impl_type(*this, BOOST_ASIO_CONCURRENCY_HINT_DEFAULT)))// 这个impl_type就是scheduler的别名
{
   
}

io_context::impl_type& io_context::add_impl(io_context::impl_type* impl)  // 这个impl_type就是scheduler的别名
{
   
  boost::asio::detail::scoped_ptr<impl_type> scoped_impl(impl);  // 指针包裹类,销毁时会delete指针所指对象
  boost::asio::add_service<impl_type>(*this, scoped_impl.get());  // 这里把scheduler添加到service_registry中
  return *scoped_impl.release();  // 解除指针与所指对象的绑定关系,这样就不会释放原本所指的对象了。
  // 为什么既然最后要解除绑定还要用scoped_impl这种智能指针来处理呢:考虑下中间异常退出的情况。
}

然后再看所调用的scheduler构造函数:

scheduler::scheduler(
    boost::asio::execution_context& ctx, int concurrency_hint)
  : boost::asio::detail::execution_context_service_base<scheduler>(ctx),
    one_thread_(concurrency_hint == 1
        || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          SCHEDULER, concurrency_hint)
        || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          REACTOR_IO, concurrency_hint)),
    mutex_(BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          SCHEDULER, concurrency_hint)),
    task_(0),  // 这个是epoll_reactor,此时初始化为空
    task_interrupted_(true),  // 一个flag,标识epoll_reactor是否处于中断状态
    outstanding_work_(0),  // 未完成的任务数
    stopped_(false),
    shutdown_(false),
    concurrency_hint_(concurrency_hint)
{
   
}

这里只需要记住outstanding_work_这个属性就行了,这个表示未完成任务数的成员属性将是决定scheduler::run()的运行状态的关键属性。
这里可以看到在scheduler构造时实际上它的触发器是空的,那这个epoll_reactor是在哪里初始化呢——在deadline_timer的构造函数中(这里仅仅是以最前面那个例子为例,并不是说epoll_reactor只能在这里面初始化,准确说epoll_reactor在许多相关的服务类中都会触发初始化)。如前面文章所说,deadline_timer的构造实际上会导致它的服务类deadline_timer_service的构造,而deadline_timer_service的构造函数中会调用epoll_reactor::init_task(),而这个函数又会调用scheduler::init_task():

  deadline_timer_service(boost::asio::io_context& io_context)
    
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值