Каков наилучший способ обеспечения действительной продолжительности жизни объекта при использовании Boost.Asio?
Много играл с Boost.Asio в последнее время. Мне очень нравится библиотека, так как она предлагает фантастический способ сжать производительность из сегодняшних многоядерных систем.
Вопрос, который я задал себе несколько раз, и я подумал о том, что стоит потратить туда время жизни/права объекта при создании асинхронных вызовов с помощью Asio.
Проблема, о которой я неоднократно говорил, заключается в том, что вам довольно часто приходится "истекать" с объектом, у которого все еще есть асинхронные обратные вызовы. Если этот объект выходит за пределы области действия до того, как обратный вызов будет вызван, все неизбежно начнется.
Для борьбы с этим я использовал шаблон boost::enable_shared_from_this
в качестве базового класса для большинства классов, основанных на asio. Это работает нормально, но это немного обременительно: обычно это также означает защиту конструктора и добавление метода factory к классу, чтобы гарантировать, что все экземпляры создаются внутри shared_ptr.
Я просто хотел узнать, как другие люди справились с этой проблемой. Я иду об этом лучше всего? Или я получил свой Asio.Foo все неправильно?
Обсудить...:)
Ответы
Ответ 1
Использование boost::enable_shared_from_this
- это в значительной степени способ сделать это. Кроме того, посмотрите на использование boost::weak_ptr
, если вам нужны ссылки на объект, который не должен сохранять объект, если они являются единственными ссылками, которые остаются.
Хороший пример использования weak_ptr
: я использую enable_shared_from_this
в моем классе сокетов, который использует boost::asio
. Структура boost::asio
- единственное, что хранит постоянные ссылки на объект, с помощью обработчиков чтения и записи. Таким образом, когда вызывается деструктор сокета, я знаю, что сокет закрыт, и я могу "делать вещи" в обработчике, чтобы очистить этот закрытый сокет. Приложение, которое использует сокет, имеет ссылку weak_ptr
на него, которое оно продвигает до shared_ptr
, когда оно хочет работать с сокетом (обычно для его записи). Это продвижение может быть проверено на предмет сбоя, если сокет ушел, хотя обработчик закрытия сокета обычно очищает все ссылки weak_ptr
соответствующим образом, прежде чем это произойдет даже.
Ответ 2
Такие вещи не ограничиваются Азио. Недавно я написал класс потока-пула (используя Boost:: Thread), который имел почти такую же проблему - потоки вызывали бы класс пула потоков, который их создавал, чтобы увидеть, какую задачу они должны были выполнить дальше, используя простой указатель к нему, и если класс потока пула был уничтожен с дочерним потоком, все еще запущенным, программа потерпит крах. Я справился с этим, вызвав interrupt
для каждого из потоков в деструкторе пула потоков, а затем ожидая, когда все они выйдут, прежде чем вернуть деструктор.
Если я понимаю ваше решение с общим указателем, похоже, он делает то же самое общее - гарантируя, что элемент нельзя уничтожить, пока он больше не понадобится. Эстетически приятное решение тоже. Я не вижу лучшего ответа на эту проблему.