asio / strand:为什么与计时器的行为不同?

时间:2012-02-15 02:57:28

标签: c++ boost-asio threadpool

我一直在关注这个excellent asio tutorial,但是他们确切地想要做什么。我的理解是它们像一个处理程序(回调)的队列一样工作,这样一个队列中的处理程序将按顺序执行。但有些实验表明我错了。有人可以解释他们到底是什么吗?

我从example 6c开始它在触发计时器之前执行PrintNum(1)PrintNum(5),每个都有1秒的延迟。 (非直观地说,如果我在启动计时器之后将PrintNum调用移动到也会发生这种情况!然后我意识到调用TimerHandler的请求没有进入 strand队列直到定时器触发。)

我的第一个变化是仅删除计时器上的链引用,但将它们保留在PrintNum(see full code on 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调用运行。我得到了我期望的输出。

我的问题来自我的第二个变体(see gist),我删除了对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 ) )
);

(你会在gist代码中看到我正在改变它,但行为基本相同。)

我在这里期望的是,strand基本上什么都不做:我一次只在一个队列中有一个处理程序(TimerHandler)。因此,我希望计时器能够独立于PrintNum调用而打开。但我看到的是PrintNum调用仍然具有优先权:所有5个必须在允许执行TimerHandler之前完成。

(值得指出的是,Drew Benton教程中的示例6c都是关于确保TimerHandler和PrintNum都不会同时运行。我的变化故意删除了这个保证;我的出发点是想要了解这个问题例6c是解决方案。)

1 个答案:

答案 0 :(得分:0)

我想我现在可以自己回答。在第二个例子中,问题与使用strand无关;如果strand->wrap被删除,行为是相同的(种类)。问题是 线程都忙了! 将线程池中的线程数增加到6并得到预期的行为:定时器在创建后触发1秒。如果有5个或更少的线程,则在TimerHandler个调用中的第一个完成之前,PrintNum将不会被调用。

正如我在问题中已经指出的那样,理解这个例子的另一个重要的事情是,strand->wrap()由计时器触发,而不是TimerHandler。当计时器关闭时,此时TimerHandler被添加到队列中。但是所有PrintNum请求都已被调用,并且所有线程都忙!

我创建了another gist,将TimerHandler置于自己的io_service中,并拥有自己的专用线程。

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调用提供一个线程,强制它们以串行方式运行。)