また、このパターンが頻繁に使用されていることもわかります。(@ Tannerのおかげで)が複数のスレッドio_service
で実行されているときに使用される理由がわかります。ただし、クラッシュの可能性をメモリ/リソースリークの可能性に置き換えるため、ライフタイムの問題がまだ残っていると思います...
boost :: bindのおかげで、shared_ptrsにバインドされたコールバックはすべてオブジェクトの「ユーザー」になるため(オブジェクトのuse_countが増加します)、未処理のコールバックがすべて呼び出されるまでオブジェクトは削除されません。
boost :: asio :: async *関数へのコールバックは、関連するタイマーまたはソケットでcancelまたはcloseが呼び出されるたびに呼び出されます。通常は、Stroustrupの最愛のRAIIパターンを使用して、デストラクタで適切なキャンセル/クローズ呼び出しを行うだけです。仕事は終わりました。
ただし、コールバックはまだshared_ptrsのコピーを保持しているため、所有者がオブジェクトを削除してもデストラクタは呼び出されません。そのため、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_;
/// shared pointer to a buffer, so that the buffer may outlive the Connection
boost::shared_ptr<std::vector<char> > read_buffer_;
void read_handler(boost::system::error_code const& error,
size_t bytes_transferred)
{
// process the read event as usual
}
/// Static callback function.
/// It ensures that the object still exists and the event is valid
/// before calling the read handler.
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> > /* read_buffer */)
{
boost::shared_ptr<Connection> pointer(ptr.lock());
if (pointer && (boost::asio::error::operation_aborted != error))
pointer->read_handler(error, bytes_transferred);
}
/// Private constructor to ensure the class is created as a shared_ptr.
explicit Connection(boost::asio::io_service& io_service) :
socket_(io_service),
strand_(io_service),
read_buffer_(new std::vector<char>())
{}
public:
/// Factory method to create an instance of this class.
static boost::shared_ptr<Connection> create(boost::asio::io_service& io_service)
{ return boost::shared_ptr<Connection>(new Connection(io_service)); }
/// Destructor, closes the socket to cancel the read callback (by
/// calling it with error = boost::asio::error::operation_aborted) and
/// free the weak_ptr held by the call to bind in the Receive function.
~Connection()
{ socket_.close(); }
/// Convert the shared_ptr to a weak_ptr in the call to bind
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
渡されます。 read_callback
shared_ptr
これは、複数io_services
が別々のタスクで実行されている場合、他のタスクが完了するまで、つまり関数が呼び出される まで、read_buffer_
が削除されないようにするためです。read_callback