我也经常看到这种模式的使用,(感谢@Tanner)我可以看出当
io_service
在
多个线程中运行时为什么要使用它。然而,我认为仍然存在生命周期问题,因为它将潜在的崩溃替换为潜在的内存/资源泄漏...
由于boost::bind,绑定到shared_ptrs的任何回调都成为对象的“用户”(增加对象的use_count),因此直到所有未完成的回调被调用后才删除对象。
boost :: asio :: async *函数的回调在相关计时器或套接字上调用cancel或close时调用。通常,您只需使用Stroustrup所钟爱的
RAII模式在析构函数中进行适当的取消/关闭调用即可完成工作。
然而,当所有者删除对象时,析构函数不会被调用,因为回调仍然持有shared_ptr的副本,因此它们的use_count将大于零,导致资源泄漏。可以通过在删除对象之前进行适当的取消/关闭调用来避免泄漏。但这并不像使用RAII和在析构函数中进行取消/关闭调用那样完全可靠。确保资源始终被释放,即使存在异常。
符合RAII的模式是使用静态函数作为回调,并在注册回调函数时传递weak_ptr到boost::bind,如下例所示:
class Connection : public boost::enable_shared_from_this<Connection>
{
boost::asio::ip::tcp::socket socket_;
boost::asio::strand strand_;
boost::shared_ptr<std::vector<char> > read_buffer_;
void read_handler(boost::system::error_code const& error,
size_t bytes_transferred)
{
}
static void read_callback(boost::weak_ptr<Connection> ptr,
boost::system::error_code const& error,
size_t bytes_transferred,
boost::shared_ptr<std::vector<char> > )
{
boost::shared_ptr<Connection> pointer(ptr.lock());
if (pointer && (boost::asio::error::operation_aborted != error))
pointer->read_handler(error, bytes_transferred);
}
explicit Connection(boost::asio::io_service& io_service) :
socket_(io_service),
strand_(io_service),
read_buffer_(new std::vector<char>())
{}
public:
static boost::shared_ptr<Connection> create(boost::asio::io_service& io_service)
{ return boost::shared_ptr<Connection>(new Connection(io_service)); }
~Connection()
{ socket_.close(); }
void Receive()
{
boost::asio::async_read(socket_, boost::asio::buffer(read_buffer_),
strand_.wrap(boost::bind(&Connection::read_callback,
boost::weak_ptr<Connection>(shared_from_this()),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
read_buffer_)));
}
};
注意:在 Connection 类中,read_buffer_ 存储为 shared_ptr,并作为 shared_ptr 传递给 read_callback 函数。这是为了确保在多个 io_services 在单独的任务中运行时,read_buffer_ 直到其他任务完成后才被删除,即在调用 read_callback 函数时。
shared_ptr
参数的“静态”辅助函数,并调用成员函数。但我看到有很多代码都是这样做的,我不敢相信它们全部都是有问题的。然而,我也找不到文档说明它是如何工作的。 - David Schwartz