提升async_*函数和shared_ptr

Dav*_*rtz 20 c++ boost shared-ptr boost-asio

我经常在代码中看到这种模式,绑定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));
 }
Run Code Online (Sandbox Code Playgroud)

使用shared_from_this()而不是使用的唯一原因this是在调用成员函数之前保持对象存活.但除非在某处有某种类型的增强魔法,因为this指针是类型的Connection*,所有都handle_Receive可以,并且返回的智能指针应该立即转换为常规指针.如果发生这种情况,那么没有任何东西可以让对象保持活力.当然,在呼叫中没有指针shared_from_this.

但是,我经常看到这种模式,我不敢相信它在我看来完全被打破了.当操作完成时,是否有一些Boost魔法会导致shared_ptr转换为常规指针?如果是这样,这是在某处记录的吗?

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

Tan*_*ury 21

简而言之,boost::bind创建boost::shared_ptr<Connection>从中返回的副本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将根据需要制作处理程序对象的副本.

  • boost :: asio io_service::~io_service:

    计划在该链接io_service或任何关联链上进行延迟调用的未经调用的处理程序对象将被销毁.

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


虽然日期(2007年),TR2网络库提案(修订版1)源自Boost.Asio.Section 5.3.2.7. Requirements on asynchronous operations提供了async_函数参数的一些细节:

在此子句中,异步操作由使用前缀命名的函数启动async_.这些功能应称为启动功能.[...]库实现可以创建处理程序参数的副本,原始处理程序参数和所有副本都是可互换的.

启动函数的参数的生命周期应视为如下:

  • 如果参数声明为const引用或by-value [...],则实现可以创建参数的副本,并且所有副本应在不迟于调用处理程序之后立即销毁.

[...]由库实现与所述发起函数的参数相关联的功能进行的任何呼叫都将在一个序列呼叫执行,使得发生呼叫1调用Ñ,其中对于所有,1≤ < Ñ,呼叫先于打电话给i + 1.

从而:

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


Igo*_* R. 5

它是这样的:

1)Boost.Bind文档说明:

"[注意:mem_fn创建的函数对象能够接受指向对象的指针,引用或智能指针作为其第一个参数;有关其他信息,请参阅mem_fn文档.]"

2)mem_fn文档:

当使用第一个参数x 调用函数对象时,该参数既不是指针也不是对相应类的引用(上例中的X),它使用get_pointer(x)从x获取指针.库作者可以通过提供适当的get_pointer重载来"注册"他们的智能指针类,允许mem_fn识别并支持它们.

因此,指针或智能指针按原样存储在活页夹中,直到调用它为止.