На форуме некоторое время назад был флейм по поводу, что лучше для определения когда у наблюдаемого объекта сменилось состояние: использовать Callback-и или периодически опрашивать объект.
У Callback-ов есть имхо 2 самых главных недостатка:
При удалении наблюдателя, Callback может быть вызван для уже несуществующего объекта
Частенько очень глубокий call stack, когда Callback вызывается. Бывает очень трудно провернуть различные манипуляции с уничтожениями объектов, изменений их состояний и т.д..
Первая проблема легко решается использованием boost::signals (заодно он позволяет использовать "перегрузку" callback-ов) вместе с boost::signals::trackable (ну или велосипедом).
А вот по второй, у меня вопрос.
Что если вызов callback-а отвязать от момента собственно происхождения события? Т.е. в момент события, callback не вызывать, а складывать его функтор в некий стек, который может быть вызван позднее, в более безопасное время.
Например.
while(1)
{
//.. длиинные операции (обработка нажатий на кнопки, сетевые сообщения и прочее)
//.. неявно наполняем callback_queue
...
//.. вызываем все отложенные callback-и
callback_queue.run();
//.. чистим
callback_queue.clear();
}
По сути это смена синхронного вызова callback-а на асинхронный.
Чем может быть опасен глобальный переход с синхронных callback-ов на асинхронные для целого проекта? В случае если очерёдность вызовов будет сохранена. И в чём этот способ может проигрывать периодическому опросу наблюдаемого объекта?
Просто я сейчас не могу придумать ни одной потенциальной проблемы, и стою перед выбором, явно специфицировать способ вызова в месте подписки на событие или неявно всех перевести на асинхронный callback.
Если речь идёт об асинхронности — значит, состояние объекта может измениться к тому моменту, когда обработчик сообщения соберётся им заняться. Это усложняет логику обработчика — он должен проверять "валидность" состояния объекта.
Здравствуйте, johny5, Вы писали:
J>При удалении наблюдателя, Callback может быть вызван для уже несуществующего объекта
Не знаю, как в C++, а в управляемых платформах (Java, .NET) независимость издателя от подписчиков легко реализуется на основе слабых ссылок (специальные ссылки с поддержкой со стороны сборщика мусора): издатель хранит слабые ссылки на подписчиков, соответственно не заморачивается на то, что какие-то из подписчиков могут уже быть мертвыми.
Здравствуйте, johny5, Вы писали:
J>На форуме некоторое время назад был флейм по поводу, что лучше для определения когда у наблюдаемого объекта сменилось состояние: использовать Callback-и или периодически опрашивать объект.
Там обсуждалась или нет нагрузка для обоих методов?
J>У Callback-ов есть имхо 2 самых главных недостатка:
J>
J> При удалении наблюдателя, Callback может быть вызван для уже несуществующего объекта
Кроме уже упомянутого решения со слабой ссылкой, есть и другой метод — наблюдатель при своём удалении обязан снять наблюдение. Я в основном использую такой вариант. Форсировать такое снятие, хоть и зависит от языка, но в большинстве несложно (в C++, например, это можно заключить в специальное поле класса с нужным деструктором).
J> Частенько очень глубокий call stack, когда Callback вызывается. Бывает очень трудно провернуть различные манипуляции с уничтожениями объектов, изменений их состояний и т.д.. J>
Дело не только в глубине стека, дело в завязке событий. Например, A вызвал у B метод, который вызвал метод в A, который вызвал метод в B. Если код B устроен так, что B при вызове метода A не восстановил целостность (по инвариантам или другую существенную для работы) состояния своего объекта — может быть худо.
Именно для таких случаев мне пришлось выносить часть действий в немедленные отложенные вызовы: для них регистрируется таймер с нулевым интервалом, который срабатывает немедленно после возвращения управления в событийный движок. Это годится не для всех применений, но для моей ситуации — вполне.
J>Что если вызов callback-а отвязать от момента собственно происхождения события? Т.е. в момент события, callback не вызывать, а складывать его функтор в некий стек, который может быть вызван позднее, в более безопасное время.
Занефиг. Только не обязательно стек. Очередь обычно не хуже, зато надёжнее в смысле доставки.
J>По сути это смена синхронного вызова callback-а на асинхронный. J>Чем может быть опасен глобальный переход с синхронных callback-ов на асинхронные для целого проекта? В случае если очерёдность вызовов будет сохранена. И в чём этот способ может проигрывать периодическому опросу наблюдаемого объекта?
Общего ответа нет. У вас может быть какая-то своя специфика. И особенно не годятся асинхронные коллбэки если нужен ответ.
Здравствуйте, netch80, Вы писали:
N>Кроме уже упомянутого решения со слабой ссылкой, есть и другой метод — наблюдатель при своём удалении обязан снять наблюдение. Я в основном использую такой вариант. Форсировать такое снятие, хоть и зависит от языка, но в большинстве несложно (в C++, например, это можно заключить в специальное поле класса с нужным деструктором).
Ага, только для управляемых платформ со сборщиком мусора это решение категорически не подходит.