Каков наилучший способ обеспечения действительной продолжительности жизни объекта при использовании 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 для каждого из потоков в деструкторе пула потоков, а затем ожидая, когда все они выйдут, прежде чем вернуть деструктор.

Если я понимаю ваше решение с общим указателем, похоже, он делает то же самое общее - гарантируя, что элемент нельзя уничтожить, пока он больше не понадобится. Эстетически приятное решение тоже. Я не вижу лучшего ответа на эту проблему.