升压async_ *函数和的shared_ptr的 [英] Boost async_* functions and shared_ptr's

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

问题描述

我经常看到在code这种模式,结合 shared_from_this 作为第一个参数的成员函数和调度使用结果的 async_ * 功能。下面是另一个问题的例子:

 无效连接::接收()
{
     提高:: ASIO :: async_read(socket_,提高:: ASIO ::缓​​存(这 - > read_buffer_)
        提高::绑定(安培;连接:: handle_Receive,
           shared_from_this(),
           提高:: ASIO ::占位符::错误
           提高:: ASIO ::占位符:: bytes_transferred));
 }

的唯一理由使用 shared_from_this()而不是这个是保持对象还活着,直到成员函数被调用。但是,除非有某种提振魔力的地方,因为这个指针类型的连接* ,这是所有 handle_Receive 可以拿,并返回智能指针应立即转换为普通指针。如果出现这种情况,没有什么保留该对象活着。而且,当然,还有呼吁没有指针 shared_from_this

不过,我已经看到了这种模式所以很多时候,我简直不敢相信,因为在我看来,它是作为彻底打破。是否有造成的shared_ptr稍后转换为普通指针,在操作完成时一些加速的魔力?如果是这样,这是记录在案的地方?

在尤其是它的地方记载,共享的指针将继续存在,直到操作完成?除非强势指针不被破坏,直到成员函数返回调用 get_pointer 雄厚的指针,然后调用返回的指针的成员函数是不够的。


解决方案

在短,的boost ::绑定创建升压的副本: :shared_ptr的<连接> 是从 shared_from_this返回()的boost :: ASIO 可以创建处理程序的副本。直到发生以下情况之一的处理程序的副本将保持活动:


  • 的处理程序已被称为一个线程从该服务的的run() run_one()调查() poll_one()成员函数被调用。

  • io_service对象被破坏。

  • io_service对象::服务,拥有该处理器是通过<关机href=\"http://www.boost.org/doc/libs/1_50_0/doc/html/boost_asio/reference/io_service__service/shutdown_service.html\"><$c$c>shutdown_service().

下面是相关摘录从文档:


  • 的boost ::绑定<一个href=\"http://www.boost.org/doc/libs/1_50_0/libs/bind/bind.html#with_functions\">documentation:


      

    这是绑定需要被复制,并用返回的函数对象内部举行的参数。



  • 的boost :: ASIO <一个href=\"http://www.boost.org/doc/libs/1_50_0/doc/html/boost_asio/reference/io_service/post.html\"><$c$c>io_service::post:


      

    io_service对象保证了处理器将只在一个线程中调用其中的的run() run_one()调查() poll_one()成员功能目前正在调用。 [...]的 io_service对象将会使的处理程序的副本的要求对象。



  • 的boost :: ASIO <一个href=\"http://www.boost.org/doc/libs/1_50_0/doc/html/boost_asio/reference/io_service/_io_service.html\"><$c$c>io_service::~io_service:


      定于递延调用上的 io_service对象,或任何相关链的

    未调用处理程序对象,被销毁。
      


      一个对象的生命周期绑定到一个连接的生命周期(或异步操作的一些其他序列),一个的shared_ptr 对象将被绑定到处理程序所有异步操作与之相关联。 [...]当一个连接结束后,所有相关的异步操作完成。相应的处理程序对象被销毁,并向对象的所有的shared_ptr 引用都毁灭了。




虽然日(2007年),在网络图书馆提案TR2(修订1)从Boost.Asio的来源。第 5.3.2.7。异步操作要求提供了一些细节的参数异步_ 功能:


  

在本节中,异步操作是由一个名为的preFIX 异步_ 功能启动。这些功能应被称为的启动功能的。 [...]该库的实现可以使处理器参数的副本,
  原来的处理程序参数,所有副本是可以互换的。


  
  

如下参数启动功能的寿命应进行处理:


  
  

      
  • 如果该参数被声明为const引用或按值[...]的实施可能使论点的副本,所有副本应在处理程序的调用之后不迟于立即销毁。

  •   

  
  

[...]由库实现与启动函数的参数相关联的功能所做的任何呼叫将顺序调用 1 要执行这样发生的呼叫叫<子>的 N 的,其中所有的 I 的,1≤ I 的&LT;的 N 的,调用 I precedes叫 I + 1


因此​​:


  • 的实施可能会创建的处理程序的副本的。在这个例子中,将复制的处理的将创建的副本的shared_ptr&LT;连接&GT; ,增加的引用计数连接实例时,处理程序的副本的仍然活着。

  • 的实施可能会破坏的处理程序调用的处理的前的。出现这种情况,如果异步操作是优秀的,当 io_serive ::服务关机或 io_service对象被破坏。在这个例子中,处理程序的副本将被破坏,从而降低连接的引用计数,并有可能导致连接实例被销毁。

  • 如果的处理的调用,那么处理器的所有副本会立即销毁一旦从处理程序执行的回报。再次,处理程序的副本将被破坏,从而降低连接的引用计数,并有可能导致它被销毁。

  • asnyc _ 的参数,会按顺序执行,而不是并发相关的功能。这包括 io_handler_deallocate io_handler_invoke 。这保证了的处理的将不同时的处理的被调用释放。在的boost :: ASIO大部分地区实现,的处理的被复制或移动到堆栈变量,允许一次执行退出发生破坏块在其被宣布。在这个例子中,这确保了连接将是的处理的的调用过程中的至少一个。
  • 引用计数

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的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆