该属性可以这样描述:如果操作被取消,它的处理程序将保证执行时出错。
例如,boost::asio::deadline_timer没有Remarks section of documentation for deadline_timer::cancel function中所述的此属性。所以有可能即使一个取消对定时器的等待操作,它的回调也会在没有错误的情况下执行。
另一方面,该属性适用于asio套接字(至少我希望如此:),因为文档中没有这样的评论。)
编辑:一个伪代码,证明在截止时间计时器中缺少此属性:
1# User calls timer.async_wait with a handler H which is to be
executed when the action finishes.
2# Time passes.
3# Timeout has been reached, asio internally inserts the handler H into
a queue for later execution, but with error code indicating success.
User is unaware of when this step takes place.
4# User calls cancel on the timer, thus would expect the handler to be
executed with an error code indicating failure.
5# Asio takes the handler H from the queue and executes it with error
code indicating success as set in the step #3.
通过在步骤#4中设置一个布尔标志然后在步骤#5中检查它来解决这个问题很容易,所以这不是问题。
答案 0 :(得分:4)
documentation和Networking Library Proposal for TR2都没有定义异步操作的术语,取消时保证错误。但是,所有异步操作(包括boost::asio::deadline_timer::async_wait()
)都会出现此行为。处理程序的设计使它们始终提供相关操作的状态。如果没有这种保证,当取消或多次操作发生时,开发人员将处于处理程序中的未知状态。
取消仅适用于尚未进行的操作。我认为文档仅针对计时器类强调了这一点,因为异步操作的可见性不同。 I / O对象上的操作具有很高的可见性。例如,可以嗅探网络以观察套接字上的异步写操作。另一方面,计时器'操作可见度低。等待操作的机制是Boost.Asio中的实现细节,并且API不提供执行操作的外部监视的能力。
在伪代码中,已达到超时,表示异步等待操作已完成。因此,由于行动已经发生,因此无法再取消。因此,不会使用boost::asio::error::operation_aborted
错误调用处理程序。至关重要的是要理解取消是一种行动,而不是一种状态变化。因此,无法查询计时器以查看是否已发生取消。此外,如果用户希望取消修改处理程序的错误代码,那么用户可能会因为异步操作的启动,完成和通知之间的时间间隔而导致继承复杂性丢失。
以下所有内容对实施细节都非常具体。在这种情况下,在系统上使用boost::asio::ip::tcp::socket::async_receive在套接字上发生异步读取,其中Boost.Asio将使用epoll作为其反应器。
reactive_socket_service_base
通知反应堆初始化任务(reactor::init_task()
)。io_service
初始化任务。这会导致将标记操作添加到io_service
的操作队列中。descriptor_data
)关联的数据结构。这个结构有自己的操作队列,实际上它被视为一个操作本身。descriptor_data
中提取文件描述符,并将其添加到文件描述符列表中,以便在反应器内进行观察。boost::asio::detail::reactive_socket_service_base::async_receive()
创建一个boost::asio::detail::reactive_socket_recv_op
对象。该对象是一个操作对象。它的perform()
成员函数将尝试从套接字读取数据,其complete()
成员函数将调用用户的完成处理程序。boost::asio::detail::reactive_socket_service_base::start_op()
,该操作将调用reactor::start_op()
。descriptor_data
,锁定特定于描述符的互斥锁,将操作推送到特定于描述符的操作队列,通知io_service
有工作,然后解锁互斥锁。 io_service::run*()
被调用。io_service
只检查其操作队列是否准备好运行任何操作。在这种情况下,初始化期间创建的标记操作位于队列中。该操作被标识为标记,表示存在反应堆,并调用reactor::run()
。epoll_wait
上阻止。当套接字的文件描述符具有活动时,则识别该事件。从事件中提取descriptor_data
,并将其推送到调用者的操作队列,因为descriptor_data
是一个操作。io_service
的操作队列中,该队列现在包含descriptor_data
操作和原始标记操作。descriptor_data
队列中弹出io_service
操作,并调用complete()
成员函数,导致epoll_reactor::descriptor_state::do_complete
运行。do_complete
调用epoll_reactor::descriptor_state::perform_io
,其中descriptor_data
操作队列中的操作被迭代,并调用perform()
。这包括在异步操作启动期间推入队列的boost::asio::detail::reactive_socket_recv_op
操作。perform()
成员函数将调用socket_ops::non_blocking_recv()
,其中尝试从套接字读取实际数据。传输的错误代码和字节存储在操作中。descriptor_data
的操作队列中删除,并通过task_io_service::post_deferred_completion
添加到io_service
。io_service
现在队列中有两个操作:完成的读操作和标记操作。 reactor
在其队列中没有任何操作。complete()
成员函数调用。在reactive_socket_recv_op::do_complete
内,用户处理程序调用错误代码和传输的字节。boost::asio::detail::reactive_socket_service_base::cancel()
使用reactor::cancel_ops
调用descriptor_data
。epoll_reactor::cancel_ops()
遍历descriptor_data
的操作队列。每个操作都从descriptor_data
的操作队列中弹出,其错误代码设置为boost::asio::error::operation_aborted
,然后添加到io_service
的操作队列。因此,取消仅通过从descriptor_data
的操作队列中删除它们来影响尚未调用的操作。如果已调用某个操作,则该操作已从descriptor_data
的操作队列中删除,并添加到io_service
操作队列中。