![]() |
注意 |
|---|---|
这些类型要求和类是取消功能的底层构建块。对于大多数用例,请考虑使用更高级别的抽象,例如 experimental::make_parallel_group 或用于 |
I/O 对象(如套接字和计时器)通过其 close 或 cancel 成员函数支持对象范围内的未决异步操作取消。但是,某些异步操作还支持单独、有针对性的取消。通过指定完成处理程序具有满足 CancellationSlot 类型要求的 关联取消槽 来启用此按操作取消。取消槽是用于传递取消请求的轻量级通道。
给定用户定义的 Handler 对象 h 的副本,如果异步操作支持取消,它将使用 get_associated_cancellation_slot 函数获取一个取消槽。例如
boost::asio::associated_cancellation_slot_t<Handler> s = boost::asio::get_associated_cancellation_slot(h);
关联取消槽必须满足 CancellationSlot 类型要求。
默认情况下,处理程序使用默认构造的 cancellation_slot,这意味着按操作取消处于禁用状态。可以通过指定嵌套类型 cancellation_slot_type 和成员函数 get_cancellation_slot() 来为特定处理程序类型自定义取消槽。
class my_handler { public: // Custom implementation of CancellationSlot type requirements. typedef my_cancellation_slot cancellation_slot_type; // Return a custom cancellation slot implementation. cancellation_slot_type get_cancellation_slot() const noexcept { return my_cancellation_slot(...); } void operator()() { ... } };
在更复杂的情况下,可以直接部分特化 associated_cancellation_slot 模板。
namespace boost { namespace asio { template <typename CancellationSlot> struct associated_cancellation_slot<my_handler, CancellationSlot> { // Custom implementation of CancellationSlot type requirements. typedef my_cancellation_slot type; // Return a custom cancellation_slot implementation. static type get(const my_handler&, const CancellationSlot& a = CancellationSlot()) noexcept { return my_cancellation_slot(...); } }; } } // namespace boost::asio
为了方便起见,可以使用 bind_cancellation_slot 函数将取消槽与处理程序关联。当将取消槽与 lambda 关联时,这尤其有用。
boost::asio::async_read(my_socket, my_buffer, boost::asio::bind_cancellation_slot( my_cancellation_slot, [](boost::system::error_code e, std::size_t n) { ... } ) );
Boost.Asio 以 cancellation_slot 及其对应的 cancellation_signal 的形式提供了即用型取消槽。这两个类实现了生产者(信号)和消费者(槽)接口的一对一配对。以下示例展示了其用法:
class session : public std::enable_shared_from_this<proxy> { ... void do_read() { auto self = shared_from_this(); socket_.async_read_some( buffer(data_), boost::asio::bind_cancellation_slot( cancel_signal_.slot(), [self](boost::system::error_code error, std::size_t n) { ... } ) ); } ... void request_cancel() { cancel_signal_.emit(boost::asio::cancellation_type::total); } ... boost::asio::cancellation_signal cancel_signal_; };
一个 cancellation_signal 包含一个槽,因此一个取消信号/槽对一次最多可用于一个操作。但是,相同的槽可以重复用于后续操作。
为了支持取消,异步操作通过调用槽的 assign 或 emplace 函数将取消处理程序安装到槽中。当发出取消信号时,将调用此处理程序。一个槽一次只能容纳一个处理程序,安装新处理程序将覆盖任何先前安装的处理程序。
发出取消信号时,调用者必须指定 取消类型。此值是一个位掩码,指示如果成功取消,取消目标必须做出何种保证。可能的位值从最弱到最强的保证是:
表 1. 取消类型
|
位 |
如果取消成功,则保证 |
此保证是最强支持保证的示例 |
|---|---|---|
|
|
操作具有未指定副作用,并且仅关闭或销毁 I/O 对象是安全的。 |
消息帧协议的有状态实现,其中异步操作发送或接收完整的消息。如果在消息正文中取消,则无法向完成处理程序报告有意义的状态。 |
|
|
操作具有明确定义的副作用,并且操作的完成处理程序指示了这些副作用。 |
组合操作,例如 |
|
|
操作没有通过 API 可观察到的副作用。 |
传输零个或非零个字节的底层系统调用。 |
例如,如果应用程序逻辑要求操作具有全部或无副作用地完成,则它应仅发出 total 取消类型。如果目标操作不支持此类型,则不会发生任何取消。
此外,更强的保证始终满足较弱保证的要求。partial 保证仍然满足 terminal 保证。total 保证满足 partial 和 terminal。这意味着当操作支持给定取消类型作为其最强保证时,它应遵守任何较弱保证的取消请求。
不应在异步操作的启动函数中发出取消请求。在操作开始之前发出的取消请求无效。同样,在完成之后发出的取消请求也无效。
发出取消信号时,线程安全规则与调用目标操作的 I/O 对象的成员函数时一样适用。对于非组合操作,这意味着可以从任何线程发出取消信号,前提是没有其他对 I/O 对象的并发调用,也没有其他并发取消信号请求。对于组合操作,必须小心确保取消请求不会与操作的中间完成处理程序并发发生。
请参阅各个异步操作的文档,了解其支持的取消类型(如果有)。目前,取消单个操作或组合操作的功能由以下组件支持:
async_read 和 async_writeasync_compose 的组合awaitable 的 C++20 协程experimental::coro 的 C++20 协程experimental::parallel_group 操作experimental::promise 类CancellationSlot、associated_cancellation_slot、bind_cancellation_slot、cancellation_signal、cancellation_slot、cancellation_state、cancellation_type、get_associated_cancellation_slot、experimental::parallel_group、experimental::make_parallel_group