![]() |
注意 |
---|---|
这些类型要求和类是取消的底层构建块。对于大多数用例,请考虑使用更高级别的抽象,例如 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_write
async_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