asio剖析(三)_添加任务-异步定时器剖析

asio剖析(三)_添加任务-异步定时器剖析

前言

我们前面已经剖析了asio同步定时器,本文将继续对异步定时器进行剖析。
asio同步定时器剖析:https://blog.csdn.net/weijianjain/article/details/129732711?spm=1001.2014.3001.5502

这篇文章由于本人对模板知识不是很熟练,剖析模板的部分总是模糊描述。

一、异步定时器剖析

我们将在同部定时器的基础上,剖析异步定时器。
异步定时器实例

#include <iostream>
#include <asio.hpp>

int main()
{
	asio::io_context io;

	asio::steady_timer t(io, asio::chrono::seconds(5));

	t.async_wait([](const asio::error_code& e) 
		{
			std::cout << "Hello, world!" << std::endl;
		});

	io.run();

	return 0;
}
	asio::io_context io;

	asio::steady_timer t(io, asio::chrono::seconds(5));

开始部分我们在同步定时器已经剖析了,就不再进行。
我们直接从t.async_wait异步函数开始,先看下t.async_wait源码.

  ///在计时器上启动异步等待。

  /**
   *此函数可用于针对
   *计时器。它总是立即返回。
   *
   *对于async_wait()的每次调用,将精确地调用所提供的处理程序
   *一次。以下情况下将调用处理程序:
   *
   *@li计时器已过期。
   *
   *@li计时器被取消,在这种情况下会将错误传递给处理程序
   *代码asio::error::operation_aborted。
   *
   *@param handler计时器到期时要调用的处理程序。副本
   *将根据需要由处理程序组成。的函数签名
   *处理程序必须是:
   *@code无效处理程序(
   *const asio::error_code&error//操作结果。
   * ); @结束代码
   *无论异步操作是立即完成还是
   *否则,处理程序将不会从该函数中调用。调用
   *将以与使用
   *asio::io_context::post().
   */
  template <typename WaitHandler>
  ASIO_INITFN_RESULT_TYPE(WaitHandler,
      void (asio::error_code))
  async_wait(ASIO_MOVE_ARG(WaitHandler) handler)
  {
      //如果您在以下行中得到错误,则意味着您的处理程序发生了错误
      //不满足WaitHandler的文档化类型要求。
    ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;

#if defined(ASIO_ENABLE_OLD_SERVICES)
    return this->get_service().async_wait(this->get_implementation(),
        ASIO_MOVE_CAST(WaitHandler)(handler));
#else // defined(ASIO_ENABLE_OLD_SERVICES)
    async_completion<WaitHandler,
      void (asio::error_code)> init(handler);

    this->get_service().async_wait(this->get_implementation(),
        init.completion_handler);

    return init.result.get();
#endif // defined(ASIO_ENABLE_OLD_SERVICES)
  }

1、anysc_wait函数第一步先进行参数校验

      //如果您在以下行中得到错误,则意味着您的处理程序发生了错误
      //不满足WaitHandler的文档化类型要求。
    ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;

ASIO_WAIT_HANDLER_CHECK宏的定义

#define ASIO_WAIT_HANDLER_CHECK( \
    handler_type, handler) \
  \
  typedef ASIO_HANDLER_TYPE(handler_type, \
      void(asio::error_code)) \
    asio_true_handler_type; \
  \
  ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \
      sizeof(asio::detail::one_arg_handler_test( \
          asio::detail::rvref< \
            asio_true_handler_type>(), \
          static_cast<const asio::error_code*>(0))) == 1, \
      "WaitHandler type requirements not met") \
  \
  typedef asio::detail::handler_type_requirements< \
      sizeof( \
        asio::detail::argbyv( \
          asio::detail::rvref< \
            asio_true_handler_type>())) + \
      sizeof( \
        asio::detail::lvref< \
          asio_true_handler_type>()( \
            asio::detail::lvref<const asio::error_code>()), \
        char(0))> ASIO_UNUSED_TYPEDEF

将校验宏展开下

typedef ASIO_HANDLER_TYPE(WaitHandler,void(asio::error_code)) asio_true_handler_type;

ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(sizeof(asio::detail::one_arg_handler_test(asio::detail::rvref<asio_true_handler_type>(),static_cast<const asio::error_code*>(0))) == 1,"WaitHandler type requirements not met")

typedef asio::detail::handler_type_requirements<sizeof(asio::detail::argbyv(asio::detail::rvref<asio_true_handler_type>())) + sizeof(asio::detail::lvref<asio_true_handler_type>()(asio::detail::lvref<const asio::error_code>()),char(0))> ASIO_UNUSED_TYPEDEF

大体分段讲一下这个宏

  • ASIO_HANDLER_TYPE
typedef ASIO_HANDLER_TYPE(WaitHandler,void(asio::error_code)) asio_true_handler_type;
...
#define ASIO_HANDLER_TYPE(ct, sig) \
  typename ::asio::async_result< \
    typename ::asio::decay<ct>::type, sig>::completion_handler_type

传进来的WaitHandler、handler这两个参数填充到ASIO_HANDLER_TYPE宏里面

typename ::asio::async_result<typename ::asio::decay<WaitHandler>::type,handler>::completion_handler_type asio_true_handler_type;
  • ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT
#  define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \
     static_assert(expr, msg);

ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT整体代码

static_assert(sizeof(asio::detail::one_arg_handler_test(asio::detail::rvref<asio_true_handler_type>(),static_cast<const asio::error_code*>(0))) == 1,"WaitHandler type requirements not met");

这里是一个static_assert断言,对WaitHandler类型要求判断是否满足。

typedef asio::detail::handler_type_requirements<sizeof(asio::detail::argbyv(asio::detail::rvref<asio_true_handler_type>())) + sizeof(asio::detail::lvref<asio_true_handler_type>()(asio::detail::lvref<const asio::error_code>()),char(0))> ASIO_UNUSED_TYPEDEF

ASIO_WAIT_HANDLER_CHECK整体转换代码

typename ::asio::async_result<typename ::asio::decay<WaitHandler>::type,handler>::completion_handler_type asio_true_handler_type;

static_assert(sizeof(asio::detail::one_arg_handler_test(asio::detail::rvref<asio_true_handler_type>(),static_cast<const asio::error_code*>(0))) == 1,"WaitHandler type requirements not met");

typedef asio::detail::handler_type_requirements<sizeof(asio::detail::argbyv(asio::detail::rvref<asio_true_handler_type>())) + sizeof(asio::detail::lvref<asio_true_handler_type>()(asio::detail::lvref<const asio::error_code>()),char(0))> ASIO_UNUSED_TYPEDEF

2、async_completion<WaitHandler,void (asio::error_code)> init(handler);剖析

    async_completion<WaitHandler,
      void (asio::error_code)> init(handler);

async_completion类声明

///用于从CompletionToken推导处理程序类型的帮助程序模板,捕获
///处理程序的本地副本,然后为
///处理程序。
template <typename CompletionToken, typename Signature>
struct async_completion
{
  /// 要用于异步操作的实际处理程序类型。
  typedef typename asio::async_result<
    typename decay<CompletionToken>::type,
      Signature>::completion_handler_type completion_handler_type;

#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  /// Constructor.
  /**
   * 构造函数创建具体的完成处理程序并创建链接
   * 在处理程序和异步结果之间。
   */
  explicit async_completion(CompletionToken& token)
    : completion_handler(static_cast<typename conditional<
        is_same<CompletionToken, completion_handler_type>::value,
        completion_handler_type&, CompletionToken&&>::type>(token)),
      result(completion_handler)
  {
  }
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  explicit async_completion(typename decay<CompletionToken>::type& token)
    : completion_handler(token),
      result(completion_handler)
  {
  }

  explicit async_completion(const typename decay<CompletionToken>::type& token)
    : completion_handler(token),
      result(completion_handler)
  {
  }
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

  /// 实际处理程序对象的副本或引用。
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  typename conditional<
    is_same<CompletionToken, completion_handler_type>::value,
    completion_handler_type&, completion_handler_type>::type completion_handler;
#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  completion_handler_type completion_handler;
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)

  /// 异步操作的启动函数的结果。
  async_result<typename decay<CompletionToken>::type, Signature> result;
};

async_completion<WaitHandler,void (asio::error_code)> init(handler);
这里定义进入了async_completion构造函数

  explicit async_completion(CompletionToken& token)
    : completion_handler(static_cast<typename conditional<
        is_same<CompletionToken, completion_handler_type>::value,
        completion_handler_type&, CompletionToken&&>::type>(token)),
      result(completion_handler)
  {
  }

token函数参是我们异步定时器传入的lambda函数,跟进下,看看这里构造做了那些操作。

completion_handler(static_cast<typename conditional<
        is_same<CompletionToken, completion_handler_type>::value,
        completion_handler_type&, CompletionToken&&>::type>(token))

completion_handler成员变量的初始化
completion_handler定义

  typename conditional<
    is_same<CompletionToken, completion_handler_type>::value,
    completion_handler_type&, completion_handler_type>::type completion_handler;

result成员变量的初始化,将completion_handler赋值到result。

3、async_wait剖析

    this->get_service().async_wait(this->get_implementation(),
        init.completion_handler);

这里是异步的重点,我们在同步定时器剖析里面讲过this->get_service()调用的函数,实现是调用deadline_timer_service类async_wait函数。

  // 在计时器上启动异步等待。
  template <typename Handler>
  void async_wait(implementation_type& impl, Handler& handler)
  {
    // 分配并构造一个操作来包装处理程序.
    typedef wait_handler<Handler> op;
    typename op::ptr p = { asio::detail::addressof(handler),
      op::ptr::allocate(handler), 0 };
    p.p = new (p.v) op(handler);

    impl.might_have_pending_waits = true;

    // 防止编译告警
    ASIO_HANDLER_CREATION((scheduler_.context(),
          *p.p, "deadline_timer", &impl, 0, "async_wait"));

    scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p);
    p.v = p.p = 0;
  }

wait_handler别名op
wait_handlder类

#define ASIO_DEFINE_HANDLER_PTR(op) \
  struct ptr \
  { \
    Handler* h; \
    op* v; \
    op* p; \
    ~ptr() \
    { \
      reset(); \
    } \
    static op* allocate(Handler& handler) \
    { \
      typedef typename ::asio::associated_allocator< \
        Handler>::type associated_allocator_type; \
      typedef typename ::asio::detail::get_hook_allocator< \
        Handler, associated_allocator_type>::type hook_allocator_type; \
      ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \
            ::asio::detail::get_hook_allocator< \
              Handler, associated_allocator_type>::get( \
                handler, ::asio::get_associated_allocator(handler))); \
      return a.allocate(1); \
    } \
    void reset() \
    { \
      if (p) \
      { \
        p->~op(); \
        p = 0; \
      } \
      if (v) \
      { \
        typedef typename ::asio::associated_allocator< \
          Handler>::type associated_allocator_type; \
        typedef typename ::asio::detail::get_hook_allocator< \
          Handler, associated_allocator_type>::type hook_allocator_type; \
        ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \
              ::asio::detail::get_hook_allocator< \
                Handler, associated_allocator_type>::get( \
                  *h, ::asio::get_associated_allocator(*h))); \
        a.deallocate(static_cast<op*>(v), 1); \
        v = 0; \
      } \
    } \
  } \

template <typename Handler>
class wait_handler : public wait_op
{
public:
  ASIO_DEFINE_HANDLER_PTR(wait_handler);

  wait_handler(Handler& h)
    : wait_op(&wait_handler::do_complete),
      handler_(ASIO_MOVE_CAST(Handler)(h))
  {
    handler_work<Handler>::start(handler_);
  }

  static void do_complete(void* owner, operation* base,
      const asio::error_code& /*ec*/,
      std::size_t /*bytes_transferred*/)
  {
    // Take ownership of the handler object.
    wait_handler* h(static_cast<wait_handler*>(base));
    ptr p = { asio::detail::addressof(h->handler_), h, h };
    handler_work<Handler> w(h->handler_);

    ASIO_HANDLER_COMPLETION((*h));

    //制作处理程序的副本,以便在
    //进行上行呼叫。即使我们不打算打电话
    //处理程序的子对象可能是关联内存的真正所有者
    //使用处理程序。因此,需要处理程序的本地副本
    //以确保任何拥有的子对象在我们
    //取消分配此处的内存。
    detail::binder1<Handler, asio::error_code>
      handler(h->handler_, h->ec_);
    p.h = asio::detail::addressof(handler.handler_);
    p.reset();

    // Make the upcall if required.
    if (owner)
    {
      fenced_block b(fenced_block::half);
      ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_));
      w.complete(handler, handler.handler_);
      ASIO_HANDLER_INVOCATION_END;
    }
  }

private:
  Handler handler_;
};
    typename op::ptr p = { asio::detail::addressof(handler),
      op::ptr::allocate(handler), 0 };

op::ptr结构体在wait_handler内部使用宏ASIO_DEFINE_HANDLER_PTR定义。

  struct ptr 
  { 
    Handler* h; 
    op* v; 
    op* p; 
    ...
  }

p = { asio::detail::addressof(handler),
      op::ptr::allocate(handler), 0 };

这里给p变量初始化,asio::detail::addressof(handler)赋值到Handler* h,op::ptr::allocate(handler)分配内存op* v,ptr结构体内部p变量初始化为0。

    p.p = new (p.v) op(handler);

p变量的p成员变量分配内存,async_wait的lamdba函数被分配到p

    impl.might_have_pending_waits = true;

将异步等待标识置为true。

# define ASIO_HANDLER_CREATION(args) (void)0
...
    // 防止编译告警
    ASIO_HANDLER_CREATION((scheduler_.context(),
          *p.p, "deadline_timer", &impl, 0, "async_wait"));

ASIO_HANDLER_CREATION防止编译告警

  // 安排和执行计时器的对象。通常是一个反应堆。
  timer_scheduler& scheduler_;
  ...
    scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p);

到schedule_timer这里就是异步添加任务到asio::io_context中
schedule_timer

template <typename Time_Traits>
void epoll_reactor::schedule_timer(timer_queue<Time_Traits>& queue,
    const typename Time_Traits::time_type& time,
    typename timer_queue<Time_Traits>::per_timer_data& timer, wait_op* op)
{
  mutex::scoped_lock lock(mutex_);

  if (shutdown_)
  {
    scheduler_.post_immediate_completion(op, false);
    return;
  }

  bool earliest = queue.enqueue_timer(time, timer, op);
  scheduler_.work_started();
  if (earliest)
    update_timeout();
}

epoll_reactor epoll处理类中。

  //将新计时器添加到队列中。如果这是
  //队列中最早的,在这种情况下,反应器的事件多路分解
  //函数调用可能需要中断并重新启动。
  bool enqueue_timer(const time_type& time, per_timer_data& timer, wait_op* op)
  {
    // 将计时器对象排队。
    if (timer.prev_ == 0 && &timer != timers_)
    {
      if (this->is_positive_infinity(time))
      {
        // 对于永不过期的计时器,不需要任何堆条目。
        timer.heap_index_ = (std::numeric_limits<std::size_t>::max)();
      }
      else
      {
          //将新计时器放在堆中的正确位置。这已经完成了
          //第一个原因是pushback()由于分配失败而可能引发。
        timer.heap_index_ = heap_.size();
        heap_entry entry = { time, &timer };
        heap_.push_back(entry);
        up_heap(heap_.size() - 1);
      }

      // 将新计时器插入活动计时器的链接列表中。
      timer.next_ = timers_;
      timer.prev_ = 0;
      if (timers_)
        timers_->prev_ = &timer;
      timers_ = &timer;
    }

    // 登记单个计时器操作。
    timer.op_queue_.push(op);

    // 仅当新添加的计时器最先到期时,才中断反应器。
    return timer.heap_index_ == 0 && timer.op_queue_.front() == op;
  }

我们可以看到在enqueue_timer函数中将async_wait的lambda函数体添加到任务中,待io::run运行完成后,退出程序。

4、 io.run()剖析

我们在前面文章已经对asio::io_context类做了简单剖析。
asio::io_context类剖析:https://blog.csdn.net/weijianjain/article/details/129732711?spm=1001.2014.3001.5502

在此基础上我们对op_queue_队列的操作剖析。

io.run();
  ...

io_context::count_type io_context::run()
{
  asio::error_code ec;
  count_type s = impl_.run(ec);
  asio::detail::throw_error(ec);
  return s;
}

...

std::size_t scheduler::run(asio::error_code& ec)
{
  ec = asio::error_code();
  // outstanding_work_原子操作,未完成工作的数量。
  // 当outstanding_work_为0,程序没有要完成的工作,停止退出,这里的退出指asio的io操作被停止,直到调用restart()恢复使用为止。
  if (outstanding_work_ == 0)
  {
    stop();
    return 0;
  }

  // 绑定线程操作
  thread_info this_thread;
  this_thread.private_outstanding_work = 0;
  thread_call_stack::context ctx(this, this_thread);

  // 加锁
  mutex::scoped_lock lock(mutex_);

  // do_run_one返回1为真,lock解锁,运行循环,n++;
  // do_run_one返回0为假,退出循环,函数结束
  std::size_t n = 0;
  for (; do_run_one(lock, this_thread, ec); lock.lock())
    if (n != (std::numeric_limits<std::size_t>::max)())
      ++n;
  return n;
}

op_queue_任务队列在do_run_one函数中不断消耗。

std::size_t scheduler::do_run_one(mutex::scoped_lock& lock,
    scheduler::thread_info& this_thread,
    const asio::error_code& ec)
{
    // stopped_结束标识
  while (!stopped_)
  {
      // 不断读取op_queue_队列,队列有数据则处理
    if (!op_queue_.empty())
    {
      // 取出对列头节点,并移除
      operation* o = op_queue_.front();
      op_queue_.pop();
      bool more_handlers = (!op_queue_.empty());

      // 如果o对列头节点为最后一个节点
      if (o == &task_operation_)
      {
        // task_interrupted_任务中断标识
        task_interrupted_ = more_handlers;

        if (more_handlers && !one_thread_)
          wakeup_event_.unlock_and_signal_one(lock);
        else
          lock.unlock();

        task_cleanup on_exit = { this, &lock, &this_thread };
        (void)on_exit;       // (void)on_exit防止编译告警,未使用函数的参数,编译期正常来说会抛出警告(warring),而在部分场景下,我们这么做可能是为了扩展等等。那么又不想看到这么多warring。

        // Run the task. May throw an exception. Only block if the operation
        // queue is empty and we're not polling, otherwise we want to return
        // as soon as possible.
        task_->run(more_handlers ? 0 : -1, this_thread.private_op_queue);
      }
      else
      {
        std::size_t task_result = o->task_result_;

        if (more_handlers && !one_thread_)
          wake_one_thread_and_unlock(lock);
        else
          lock.unlock();

        // 确保在块退出时减少未完成工作的计数。
        work_cleanup on_exit = { this, &lock, &this_thread };
        (void)on_exit;

        // 完成操作。可能引发异常。删除对象。
        o->complete(this, ec, task_result);

        return 1;
      }
    }
    else
    {
      wakeup_event_.clear(lock);  // 重置事件。
      wakeup_event_.wait(lock);   // 等待事件发出信号。
    }
  }

  return 0;
}

我们直接在 o->complete剖析

        // 完成操作。可能引发异常。删除对象。
        o->complete(this, ec, task_result);

我们之前异步插入的队列元素,在这里会执行

  void complete(void* owner, const asio::error_code& ec,
      std::size_t bytes_transferred)
  {
    func_(owner, this, ec, bytes_transferred);
  }

这里的func函数指针执行写在async_wait里面的lamdba函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值