Лучшие практики асинхронного вызова C++
Я работаю с boost::asio. Я написал класс, отвечающий за асинхронное чтение из сокета. В моем приложении io_service может останавливаться и запускаться много раз за один запуск приложения. Поэтому я должен беспокоиться об утечке памяти, когда служба остановлена. Я пришел к двум решениям:
Класс, запрашивающий асинхронный запрос, предоставляет функцию с буфером для использования в asio reads и отвечает за его освобождение. Это очевидное решение, но мне оно не нравится. Передача параметра, который вам не нужен, в функции выглядит очень странно.
Умный указатель привязан к обратному вызову. Пример здесь: http://pastebin.com/p8nQ5NFi
Сейчас я использую второе решение, но как бы я ни чувствовал, я изобрел колесо. Какова общая практика для очистки буфера в асинхронном вызове? Есть ли скрытые проблемы в моем подходе?
1 ответ
shared_ptr
Подход довольно распространен. Однако вместо прохождения shared_ptr
в качестве дополнительного аргумента bind
, можно пройти shared_ptr
в качестве объекта экземпляра вместо this
,
boost::shared_ptr< my_class > ptr( this );
boost::asio::async_read( stream, buffer,
boost::bind( &my_class::read_handler, ptr,
boost::asio::placeholders::error
boost::asio::placeholders::bytes_transferred ) );
Часто, так как экземпляр будет управляться через shared_ptr
который может или не может быть в контексте this
, это хорошая идея, чтобы использовать Boost.SmartPointer's enable_shared_from_this
, Когда класс наследует отboost::enable_shared_from_this
это обеспечиваетshared_from_this()
функция-член, которая возвращает действительный shared_ptr
экземпляр для this
,
class my_class: public boost::enable_shared_from_this< my_class >
{
void read()
{
boost::asio::async_read( stream, buffer,
boost::bind( &my_class::read_handler, shared_from_this(),
boost::asio::placeholders::error
boost::asio::placeholders::bytes_transferred ) );
}
};
boost::shared_ptr< my_class > foo( new my_class() );
foo->read();
В этом фрагментеfoo.get()
а такжеfoo->shared_from_this()
оба указывают на один и тот же экземпляр. Это помогает избежать трудностей с обнаружением утечек памяти. Например, в исходном примере кода утечка памяти происходит в Protocol::AsyncReadMessage
если AsyncReadHandler
копирует конструктор бросает при попытке вызвать AsyncReadMessage
, Асинхронный TCP-сервер Boost.Asio для дневного времени и многие примеры показывают enable_shared_from_this
используется в Boost.Asio. Для более глубокого понимания этот вопрос конкретно охватывает асинхронные функции Boost.Asio и shared_ptr
,
Кроме того, в исходном коде может быть проще сделать Protocol::AsyncHelper
класс шаблона вместо того, чтобы иметь класс без шаблонов с функциями-членами шаблона. Это позволило бы AsyncHelper
принимать протокол, поток и обработчик в качестве аргументов конструктора, сохраняя их как переменные-члены. Кроме того, это делает bind
вызовы немного легче читать, так как это уменьшает количество аргументов, которые необходимо передать, и поскольку функции-члены больше не являются шаблонами, нет необходимости указывать их полный тип. Вот быстрый пример на примере.