Asio / Strand: почему поведение с таймером отличается? - PullRequest
0 голосов
/ 15 февраля 2012

Я следил вместе с этим превосходным учебным пособием по asio , но запутался именно с тем, что делают пряди. Насколько я понимаю, они работали как очередь для набора обработчиков (обратных вызовов), так что обработчики в такой очереди выполнялись бы по порядку. Но некоторые эксперименты показывают, что я не прав. Может кто-нибудь объяснить, кто они на самом деле?

Я начинаю с , пример 6c Он выполняет от PrintNum(1) до PrintNum(5), каждый с задержкой в ​​1 секунду, перед запуском таймера. (Необязательно, это также происходит, если я переместил вызовы PrintNum после запуска таймера! Затем я понял, что запрос вызова TimerHandler не попадает в очередь очереди до тех пор, пока не сработает таймер.)

Моим первым вариантом было удалить ссылки на нити только по таймеру, но оставить их в PrintNum ( см. Полный код в gist ):

strand->post( boost::bind( &PrintNum, 1 ) );
strand->post( boost::bind( &PrintNum, 2 ) );
strand->post( boost::bind( &PrintNum, 3 ) );
strand->post( boost::bind( &PrintNum, 4 ) );
strand->post( boost::bind( &PrintNum, 5 ) );

boost::shared_ptr< boost::asio::deadline_timer > timer(
        new boost::asio::deadline_timer( *io_service )
);
timer->expires_from_now( boost::posix_time::seconds( 1 ) );
timer->async_wait( boost::bind( &TimerHandler, _1, timer ) );

Теперь таймер работает независимо от вызовов PrintNum. Я получаю вывод, который ожидал.

У меня вопрос ко второму варианту ( см. Суть ), где я удалил вызовы прядей для PrintNum, но сохранил их в таймере:

io_service->post( boost::bind( &PrintNum, 1 ) );
io_service->post( boost::bind( &PrintNum, 2 ) );
io_service->post( boost::bind( &PrintNum, 3 ) );
io_service->post( boost::bind( &PrintNum, 4 ) );
io_service->post( boost::bind( &PrintNum, 5 ) );

boost::shared_ptr< boost::asio::deadline_timer > timer(
        new boost::asio::deadline_timer( *io_service )
);
timer->expires_from_now( boost::posix_time::milliseconds( 1000 ) );
timer->async_wait(
        strand->wrap( boost::bind( &TimerHandler, _1, timer, strand ) )
);

(Вы увидите в коде сущности, что я немного перемешал его, но поведение в основном такое же.)

То, что я ожидал здесь, это то, что цепочка в основном ничего не делает: я только один обработчик (TimerHandler) в очереди цепочки за раз. Поэтому я ожидал, что таймер отключится независимо от вызовов PrintNum. Но я вижу, что вызовы PrintNum по-прежнему имеют приоритет: все 5 должны завершиться, прежде чем TimerHandler будет разрешено выполнить.

(Стоит отметить, что пример 6c в учебном пособии Дрю Бентона был посвящен обеспечению того, чтобы ни TimerHandler, ни PrintNum не работали одновременно. Мои варианты намеренно снимают эту гарантию; моей отправной точкой было желание понять проблему, заключающуюся в том, что Пример 6с является решением.)

1 Ответ

0 голосов
/ 16 февраля 2012

Я думаю, что могу ответить сам сейчас.Во втором примере проблема не связана с использованием прядей;поведение (вроде) идентично, если strand->wrap удален.Проблема в все потоки заняты! Увеличьте число потоков в пуле потоков до 6, и мы получим ожидаемое поведение: таймер срабатывает через 1 секунду после его создания.Когда имеется 5 или меньше потоков, то TimerHandler не будет вызываться до тех пор, пока не закончится первый из PrintNum вызовов.

Другая важная вещь для понимания этого примера, как я уже отмечал ввопрос в том, что strand->wrap() запускается таймером, а не TimerHandler.Когда таймер отключается, именно в этот момент TimerHandler добавляется в очередь.Но все PrintNum запросы уже были вызваны, и все потоки заняты!

Я создал еще одну сущность , которая помещает TimerHandler в свой собственный io_service со своим собственным выделеннымthread.

boost::shared_ptr< boost::asio::io_service > io_service2(
        new boost::asio::io_service
);
boost::shared_ptr< boost::asio::io_service::work > work2(
        new boost::asio::io_service::work( *io_service2 )
);
boost::shared_ptr< boost::asio::io_service::strand > strand2(
        new boost::asio::io_service::strand( *io_service2 )
);

...

boost::thread_group worker_threads;
for( int x = 0; x < 2; ++x )
{
        worker_threads.create_thread( boost::bind( &WorkerThread, x==0? io_service2 : io_service ) );
}

...

boost::shared_ptr< boost::asio::deadline_timer > timer(
        new boost::asio::deadline_timer( *io_service2 )
);
timer->expires_from_now( boost::posix_time::milliseconds( 1000 ) );
timer->async_wait(
        strand2->wrap( boost::bind( &TimerHandler, _1, timer, strand2 ) )
);

Теперь таймер будет работать надежно независимо от того, сколько вызовов занимает PrintNum вызов.(Чтобы подчеркнуть этот момент, я даю только один поток для всех вызовов PrintNum для совместного использования, заставляя их запускаться последовательно.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...