c++ - 确保在所有异步读/写完成后调用一次 on_close() 的设计模式?
问题描述
这个问题是从 Boost ASIO (C++) 的上下文中提出的。
假设您正在使用库在套接字上执行一些异步 i/o,其中:
- 你总是在等待接收数据
- 你偶尔会发送一些数据
由于您总是在等待接收数据(例如,您async_read()
从完成处理程序触发另一个数据),因此在任何给定时间,您都将拥有:
- 正在进行的异步读取操作
- 正在进行的异步读取操作和正在进行的异步写入操作
on_close()
现在假设您想在连接关闭时调用其他函数。在 Boost ASIO 中,连接错误或cancel()
将导致任何未完成的异步读取/写入向您的完成处理程序提供错误。但是不能保证您是在场景 1. 还是 2. 中,也不能保证在读取之前写入会出错,反之亦然。因此,为了实现这一点,我只能想象添加两个变量,分别由is_reading
和设置为 true ,并由完成处理程序设置为 false 。然后,从任一完成处理程序中,当出现错误并且我认为连接可能正在关闭时,我会检查是否仍有相反方向的异步操作,如果没有则调用。is_writing
async_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()
在最后一个异步操作完成后调用?
解决方案
最常见的模式之一是使用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 (请务必关注评论链接)。