正如 Igor 在 cmets 中提到的,完成处理程序已经排队。
这种情况是操作执行与调用处理程序之间的时间分离的结果。 io_service::run()、io_service::run_one()、io_service::poll() 和 io_service::poll_one() 的文档专门针对处理程序,而不是操作。在该场景中,socket::async_connect() 操作和deadline_timer::async_wait() 操作在同一个事件循环迭代中完成。这会导致两个处理程序都以未指定的顺序添加到 io_service 以进行延迟调用。
考虑以下强调场景的 sn-p:
void handle_wait(const boost::system::error_code& error)
{
if (error) return;
socket_.close();
}
timer_.expires_from_now(boost::posix_time::seconds(30));
timer_.async_wait(&handle_wait);
socket_.async_connect(endpoint_, handle_connect);
boost::this_thread::sleep(boost::posix_time::seconds(60));
io_service_.run_one();
当io_service_.run_one() 被调用时,socket::async_connect() 和deadline_timer::async_wait() 操作可能已经完成,导致handle_wait 和handle_connect 准备好以未指定的顺序从io_service 中调用。为了正确处理这个未指定的顺序,需要在handle_wait() 和handle_connect() 中发生额外的逻辑来查询当前状态,并确定是否调用了另一个处理程序,而不是仅仅依赖于状态(error_code)操作。
确定其他处理程序是否已调用的最简单方法是:
- 在
handle_connect() 中,通过is_open() 检查套接字是否仍然打开。如果套接字仍然打开,则 handle_timer() 尚未被调用。向handle_timer() 表明handle_connect() 已运行的一种简洁方法是更新到期时间。
- 在
handle_timer(),检查是否过期。如果这是真的,那么handle_connect() 还没有运行,所以关闭套接字。
生成的处理程序可能如下所示:
void handle_wait(const boost::system::error_code& error)
{
// On error, return early.
if (error) return;
// If the timer expires in the future, then connect handler must have
// first.
if (timer_.expires_at() > deadline_timer::traits_type::now()) return;
// Timeout has occurred, so close the socket.
socket_.close();
}
void handle_connect(const boost::system::error_code& error)
{
// The async_connect() function automatically opens the socket at the start
// of the asynchronous operation. If the socket is closed at this time then
// the timeout handler must have run first.
if (!socket_.is_open()) return;
// On error, return early.
if (error) return;
// Otherwise, a connection has been established. Update the timer state
// so that the timeout handler does not close the socket.
timer_.expires_at(boost::posix_time::pos_infin);
}
Boost.Asio 提供了一些examples 用于处理超时。