【问题标题】:What's the lifetime of boost::asio::ip::tcp::resolver::iterator from async_resolve?来自 async_resolve 的 boost::asio::ip::tcp::resolver::iterator 的生命周期是多少?
【发布时间】:2018-04-13 09:28:01
【问题描述】:

当我调用boost::asio::ip::tcp::resolver::async_resolve 时,我的处理程序会收到一个ip::tcp::resolver::iterator,它会遍历一个或多个ip::tcp::resolver::entries他们的寿命是多少,让他们活着的把柄是什么?

例如,如果我得到第一个 entry 并启动 tcp::async_connect 到它,那么在 async_connect 处理程序中,我可以迭代到下一个 entry 并启动另一个 async_connect 到下一个条目(当然,只要我将iterator 传递给async_connect 处理程序)?

resolver::iteratorresolver::entries 何时清理?我是否必须做一些特别的事情,或者只是让它们超出范围而不被任何回调闭包所持有?

(我知道我可以在我的async_resolve 处理程序中运行所有resolver::entries 并将它们存储在智能指针结构或其他任何东西中,以便我控制它们的生命周期,但如果asio::ip::tcp::resolver 已经在处理它,如果我让它完成它的工作,我的代码会更简单。)

【问题讨论】:

  • 有趣的是你应该问。我回答了另一个问题earlier today,出于谨慎考虑,我避免将迭代器传递给另一个函数。我现在知道这不是必需的:)

标签: c++ sockets boost tcp boost-asio


【解决方案1】:

解构问题

迭代器具有值语义。因此,它们的生命周期总是与周围对象的生命周期或它们的存储持续时间绑定(堆栈用于自动,堆用于动态,有时甚至是其他对象,例如静态)。

我想你想知道迭代器的有效性

好吧,the documentation shows 迭代器类别是 forward iterator。前向迭代器具有“多次传递保证”,允许重复取消引用迭代器的副本,产生相同的结果¹。

所以,我们已经完成了一半:保留迭代器仍然可以。但是,当然,与任何其他 [forward] 迭代器一样,我们必须考虑 iterator invalidation

因此,真正的问题归结为:解析器迭代器何时失效

我们知道什么?

resolve 函数实现的用例是连接。对于连接来说,第一个有效的端点就足够了,所以它实际上不需要保留一个列表。

本着按需付费²的精神,解析器将状态保持比所需时间更长是没有意义的。另一方面,如果不支持 Multipass,则将迭代器放在 ForwardIterator 类别中是没有意义的。

文档什么也没说。我们只有一个办法:深入研究代码

深入了解代码

我们发现表面之下的一些步骤:asio/detail/resolver_service.hpp:73

  // Asynchronously resolve a query to a list of entries.
  template <typename Handler>
  void async_resolve(implementation_type& impl,
      const query_type& query, Handler& handler)
  {
    // Allocate and construct an operation to wrap the handler.
    typedef resolve_op<Protocol, Handler> op;
    typename op::ptr p = { boost::asio::detail::addressof(handler),
      boost_asio_handler_alloc_helpers::allocate(
        sizeof(op), handler), 0 };

resolve_op表示迭代器是使用basic_resolver_iterator.hpp::create创建的

这让我们找到了答案:在line 251

  typedef std::vector<basic_resolver_entry<InternetProtocol> > values_type;
  boost::asio::detail::shared_ptr<values_type> values_;
  std::size_t index_;

因此,只要您保留有效迭代器(而不是结束迭代器)的副本,您就可以继续取消引用它。它甚至会为每个解析器条目保留一份查询参数(host_nameservice_name)的副本。这似乎有点浪费,但我想在设计缓存方案时会派上用场。

总结:

  • 问。解析器迭代器何时失效?
  • A. 当有效迭代器的最后一个副本被破坏时。

这意味着“它们始终保持有效”(如果它们曾经有效的话)。


¹ 而不是例如输入迭代器

² 通常,C++ 实现遵循 零开销原则:你不使用什么,你就不用付费 [The Design and Evolution of C++, 1994]

【讨论】:

  • 啊,你找到了它 (basic_resolver_iterator.hpp:251)...我迷失在标题、IPP 文件等。只要我需要,shared_ptr 就是我用来保存所有信息的工具,所以很明显我准备好基本上重复努力。再进一步,我看到内存是为gethostbyname 返回的信息分配的,所以我认为它已被正确释放,并且当所有有效的迭代器都消失并且smart_ptr&lt;values_type&gt; 通知时可能会发生这种情况。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多