首页 > 解决方案 > 确保在所有异步读/写完成后调用一次 on_close() 的设计模式?

问题描述

这个问题是从 Boost ASIO (C++) 的上下文中提出的。

假设您正在使用库在套接字上执行一些异步 i/o,其中:

由于您总是在等待接收数据(例如,您async_read()从完成处理程序触发另一个数据),因此在任何给定时间,您都将拥有:

  1. 正在进行的异步读取操作
  2. 正在进行的异步读取操作和正在进行异步写入操作

on_close()现在假设您想在连接关闭时调用其他函数。在 Boost ASIO 中,连接错误或cancel()将导致任何未完成的异步读取/写入向您的完成处理程序提供错误。但是不能保证您是在场景 1. 还是 2. 中,也不能保证在读取之前写入会出错,反之亦然。因此,为了实现这一点,我只能想象添加两个变量,分别由is_reading和设置为 true ,并由完成处理程序设置为 false 。然后,从任一完成处理程序中,当出现错误并且我认为连接可能正在关闭时,我会检查是否仍有相反方向的异步操作,如果没有则调用。is_writingasync_read()async_write()on_close()

代码,或多或少:

atomic_bool is_writing;
atomic_bool is_reading;

...

void read_callback(error_code& error, size_t bytes_transferred)
{
  is_reading = false;

  if (error)
  {
    if (!is_writing) on_close();
  }
  else
  {
    process_data(bytes_transferred);
  
    async_read(BUF_SIZE);  // this will set is_reading to true
  }
}

void write_callback(error_code& error, size_t bytes_transferred)
{
  is_writing = false;

  if (error)
  {
    if (!is_reading) on_close();
  }
}

假设这是一个单线程应用程序,但线程正在处理多个套接字,因此您不能让线程结束。

有没有更好的方法来设计这个?确保on_close()在最后一个异步操作完成后调用?

标签: c++asynchronousdesign-patternsioboost-asio

解决方案


最常见的模式之一是使用enable_shared_from_this所有完成处理程序(“延续”)并将其绑定到它。

这样,如果异步调用链结束(无论是由于错误还是常规完成),shared_ptr 裁判将被释放。

你可以在这个网站上看到很多我使用 Asio/Beast 的例子

您可以将关闭逻辑放在析构函数中,或者如果这也涉及异步调用,您可以将其发布在同一个链/链上。

先进的想法

如果您的流量是全双工的,并且一侧以需要取消另一方向的方式发生故障,您可以post取消链,异步调用将中止(例如使用error_code boost::asio::error::operation_aborted)。

更多涉及的是创建自定义 IO 服务,其中某些“后端”实体的生命周期由“句柄”类型控制。这可能通常是矫枉过正,但如果你正在编写一个将在更多地方使用的基础框架,你可能会考虑它。我认为这是一个很好的开端:How to design proper release of a boost::asio socket or wrapper (请务必关注评论链接)。


推荐阅读