пятница, 22 июля 2016 г.

[prog.c++] Удивлен поведением asio::io_service::run и asio::io_service::stopped

Попробовал сделать вспомогательный класс, который бы содержал внутри себя экземпляр asio::io_service и пул рабочих нитей, на которых бы дергались методы asio::io_service::run. Как водится, документацию внимательно прочитал только после того, как обнаружил, что ничего не работает. Ну а когда прочитал, был несколько удивлен.

Оказывается io_service::run возвращает управление сразу же, если сейчас в io_service нет никаких задач. Т.е., если нет ни таймеров, ни отслеживаемых событий ввода-вывода, то io_service::run сразу же завершает свою работу и возвращает управление.

С одной стороны, такое поведение можно понять. Если приложение полностью базируется на asio, то в нем всегда будет что-то в очереди задач io_service. А когда не будет, то значит приложение завершило все свои действия и можно прекращать работу.

Но вот в моем случае оказалось не так. У меня необходимость выполнения операций ввода-вывода возникает время от времени. Когда возникает, я выдаю задачи io_service, когда не возникает, io_service ничего не делает.

Первоначальная задумка была в том, чтобы создать пул рабочих потоков для asio. Каждый поток дернул бы io_service::run и заснул бы, если у asio нет задач. Задачи появились -- потоки проснулись, отработали свое и заснули бы вновь. Перед завершением работы я бы дернул io_service::stop, рабочие потоки бы проснулись, на них бы завершились вызовы io_service::run и все остались бы довольны.

Но нет, так просто не получилось. Когда задач для asio нет, рабочие потоки сразу же вылетают из io_service::run и сразу же завершают свою работу. Чтобы они не завершали свою работу сразу же, нужно либо делать в рабочих потоках цикл, в котором постоянно дергается io_service::run, либо придумывать что-то еще.

В итоге сделал схему, в которой создается один лишний таймер, срабатывающий раз в час. Срабатывающий только для того, чтобы перезадать себя самого еще на час. А потом еще на час и т.д.

Наличие этого таймера создает для io_service видимость наличия работы, которая еще не выполнена. Что и не позволяет вызовам io_service::run на рабочих потоках завершаться сразу же. Завершение происходит только тогда, когда в конце работы всего приложения явным образом вызывается io_service::stop.

В общем, был удивлен поведению io_service::run. Такое впечатление, что ее задизайнили под такие сценарии использования, когда у приложения нет никаких других задач, кроме ввода-вывода. Еще больше я удивился тому, что io_service::stopped возвращает true не только в том случае, когда io_service::stop вызывали явно. Но и в том случае, когда io_service::stop вообще не вызывали, а у io_service просто не оказалось никакой работы.

Остается надеяться, что это просто я из-за недостатка опыта работы с asio что-то не так понял, и что пул рабочих потоков для io_service::run можно организовать более прямым образом, без обходных маневров с липовыми таймерами.

Комментариев нет: