提升 async_* 函数和 shared_ptr 的

Boost async_* functions and shared_ptr#39;s(提升 async_* 函数和 shared_ptr 的)

本文介绍了提升 async_* 函数和 shared_ptr 的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常在代码中看到这种模式,将 shared_from_this 作为第一个参数绑定到成员函数并使用 async_* 函数分派结果.这是另一个问题的示例:

void Connection::Receive(){boost::asio::async_read(socket_,boost::asio::buffer(this->read_buffer_),boost::bind(&Connection::handle_Receive,shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));}

使用 shared_from_this() 而不是 this 的唯一原因是让对象保持活动状态直到成员函数被调用.但是除非在某处有某种提升魔法,因为 this 指针是 Connection* 类型,这就是 handle_Receive 可以接受的,并且智能返回的指针应立即转换为常规指针.如果发生这种情况,则没有什么可以使对象保持活动状态.而且,当然,在调用 shared_from_this 时没有指针.

然而,我经常看到这种模式,我不敢相信它像我看起来的那样完全破碎.当操作完成时,是否有一些 Boost 魔法导致 shared_ptr 稍后转换为常规指针?如果是这样,这是否记录在某处?

特别是,是否在某处记录了共享指针将一直存在直到操作完成?对强指针调用 get_pointer 然后在返回的指针上调用成员函数是不够的,除非强指针在成员函数返回之前不会被销毁.

解决方案

简而言之,boost::bind 创建了 boost::shared_ptr 的副本从 shared_from_this() 返回,并且 boost::asio 可以创建处理程序的副本.处理程序的副本将保持活动状态,直到发生以下情况之一:

  • 处理程序已被服务的run()run_one()poll() 的线程调用>poll_one() 成员函数已被调用.
  • io_service 被销毁.
  • 拥有处理程序的 io_service::service 通过 shutdown_service().

以下是文档的相关摘录:

  • boost::bind 文档:><块引用>

    bind 接受的参数由返回的函数对象在内部复制和保存.

  • boost::asio io_service::post:

    <块引用>

    io_service 保证处理程序只会在 run(), run_one(), 的线程中被调用>poll()poll_one() 成员函数当前正在被调用.[...] io_service 将根据需要制作 handler 对象的副本.

  • boost::asio io_service::~io_service:

    <块引用>

    计划为在 io_service 或任何相关链上延迟调用的未调用处理程序对象将被销毁.

    在对象的生命周期与连接(或其他一些异步操作序列)的生命周期相关的情况下,对象的 shared_ptr 将绑定到与其关联的所有异步操作的处理程序中.[...] 当单个连接结束时,所有相关的异步操作都完成.相应的处理程序对象被销毁,并且对对象的所有 shared_ptr 引用都被销毁.

<小时>

在日期 (2007) 期间,网络图书馆提案 TR2(修订版 1) 源自 Boost.Asio.<代码>5.3.2.7 节.对异步操作的要求async_ 函数的参数提供了一些详细信息:

<块引用>

在这个子句中,异步操作由一个以前缀async_命名的函数启动.这些函数应称为启动函数.[...] 库实现可能会复制处理程序参数,并且原始处理程序参数和所有副本都可以互换.

初始化函数的参数生命周期应如下处理:

  • 如果参数被声明为 const 引用或按值 [...] 实现可以制作参数的副本,并且所有副本应不迟于调用处理程序后立即销毁.

[...] 库实现对与启动函数的参数相关联的函数的任何调用都将被执行,这样调用就会按顺序调用1 到调用n,其中对于所有的i,1 ≤ i <n,在调用i+1之前调用i.

因此:

  • 实现可能会创建一个处理程序的副本.在示例中,复制的 handler 将创建 shared_ptr 的副本,增加 Connection 实例的引用计数,同时复制handler 仍然存活.
  • 实现可能会在调用 handler 之前销毁 handler.如果在 io_serive::service 关闭或 io_service 销毁时异步操作未完成,则会发生这种情况.在示例中,handler 的副本将被销毁,从而减少 Connection 的引用计数,并可能导致 Connection 实例被销毁.
  • 如果 handler 被调用,那么一旦执行从处理程序返回,处理程序的所有副本将立即被销毁.同样,处理程序的副本将被销毁,从而减少 Connection 的引用计数,并可能导致它被销毁.
  • asnyc_ 的参数关联的函数将按顺序执行,而不是并发执行.这包括 io_handler_deallocateio_handler_invoke.这保证了 handlerhandler 被调用时不会被释放.在 boost::asio 实现的大部分区域,handler 被复制或移动到堆栈变量,一旦执行退出声明它的块,就允许销毁.在示例中,这确保了 Connection 的引用计数在 handler 调用期间至少为 1.

I frequently see this pattern in code, binding shared_from_this as the first parameter to a member function and dispatching the result using an async_* function. Here's an example from another question:

void Connection::Receive()
{
     boost::asio::async_read(socket_,boost::asio::buffer(this->read_buffer_),
        boost::bind(&Connection::handle_Receive, 
           shared_from_this(),
           boost::asio::placeholders::error,
           boost::asio::placeholders::bytes_transferred));
 }

The only reason to use shared_from_this() instead of this is to keep the object alive until the member function gets called. But unless there's some kind of boost magic somewhere, since the this pointer is of type Connection*, that's all handle_Receive can take, and the smart pointer returned should be converted to a regular pointer immediately. If that happens, there's nothing to keep the object alive. And, of course, there's no pointer in calling shared_from_this.

However, I've seen this pattern so often, I can't believe it's as completely broken as it seems to me. Is there some Boost magic that causes the shared_ptr to be converted to a regular pointer later, when the operation completes? If so, is this documented somewhere?

In particular, is it documented somewhere that the shared pointer will remain in existence until the operation completes? Calling get_pointer on the strong pointer and then calling the member function on the returned pointer is not sufficient unless the strong pointer isn't destroyed until the member function returns.

解决方案

In short, boost::bind creates a copy of the boost::shared_ptr<Connection> that is returned from shared_from_this(), and boost::asio may create a copy of the handler. The copy of the handler will remain alive until one of the following occurs:

  • The handler has been called by a thread from which the service's run(), run_one(), poll() or poll_one() member function has been invoked.
  • The io_service is destroyed.
  • The io_service::service that owns the handler is shutdown via shutdown_service().

Here are the relevant excerpts from the documentation:

  • boost::bind documentation:

    The arguments that bind takes are copied and held internally by the returned function object.

  • boost::asio io_service::post:

    The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked. [...] The io_service will make a copy of the handler object as required.

  • boost::asio io_service::~io_service:

    Uninvoked handler objects that were scheduled for deferred invocation on the io_service, or any associated strand, are destroyed.

    Where an object's lifetime is tied to the lifetime of a connection (or some other sequence of asynchronous operations), a shared_ptr to the object would be bound into the handlers for all asynchronous operations associated with it. [...] When a single connection ends, all associated asynchronous operations complete. The corresponding handler objects are destroyed, and all shared_ptr references to the objects are destroyed.


While dated (2007), the Networking Library Proposal for TR2 (Revision 1) was derived from Boost.Asio. Section 5.3.2.7. Requirements on asynchronous operations provides some details for the arguments to async_ functions:

In this clause, an asynchronous operation is initiated by a function that is named with the prefix async_. These functions shall be known as initiating functions. [...] The library implementation may make copies of the handler argument, and the original handler argument and all copies are interchangeable.

The lifetime of arguments to initiating functions shall be treated as follows:

  • If the parameter is declared as a const reference or by-value [...] the implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the handler.

[...] Any calls made by the library implementation to functions associated with the initiating function's arguments will be performed such that calls occur in a sequence call1 to calln, where for all i, 1 ≤ i < n, calli precedes call i+1.

Thus:

  • The implementation may create a copy of the handler. In the example, the copied handler will create a copy of the shared_ptr<Connection>, increasing the reference count of the Connection instance while the copies of handler remain alive.
  • The implementation may destroy the handler prior to invoking handler. This occurs if the async operation is outstanding when io_serive::service is shutdown or the io_service is destroyed. In the example, the copies of handler will be destroyed, decreasing the reference count of Connection, and potentially causing the Connection instance to be destroyed.
  • If handler is invoked, then all copies of handler will immediately be destroyed once execution returns from the handler. Again, the copies of handler will be destroyed, decreasing the reference count of Connection, and potentially causing it to be destroyed.
  • The functions associated with the asnyc_'s arguments, will be executed sequentially, and not concurrent. This includes io_handler_deallocate and io_handler_invoke. This guarantees that the handler will not be deallocated while the handler is being invoked. In most areas of the boost::asio implementation, the handler is copied or moved to stack variables, allowing the destruction to occur once execution exits the block in which it was declared. In the example, this ensures that the reference count for Connection will be at least one during the invocation of the handler.

这篇关于提升 async_* 函数和 shared_ptr 的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:提升 async_* 函数和 shared_ptr 的