3

我在使用deadline_timer 和io_service::post 时遇到问题,如下所示:

#include "boost/asio.hpp"
#include "boost/thread.hpp"
int main()
{
    boost::asio::io_service io_service;

    boost::asio::deadline_timer timer1(io_service);
    boost::asio::deadline_timer timer2(io_service);

    timer1.expires_from_now(boost::posix_time::seconds(1));
    timer1.async_wait([](const boost::system::error_code& error) {
        boost::this_thread::sleep(boost::posix_time::seconds(5));
        printf("1 ");
    });

    timer2.expires_from_now(boost::posix_time::seconds(2));
    timer2.async_wait([](const boost::system::error_code& error) {
        printf("2 ");
    });

    boost::thread t([&io_service]() {
        boost::this_thread::sleep(boost::posix_time::seconds(5));
        io_service.post([]() {
            printf("3 ");
        });
        io_service.post([]() {
            printf("4 ");
        });
    });

    io_service.run();
    t.join();
    getchar();

    return 0;
}

我认为结果是“1 2 3 4”,但结果是“1 3 4 2”。任何人都可以向我展示如何使用 boost 库执行 timer2(print "2") 的回调,结果是 "1 2 3 4"(并且不要更改 timer1 和 timer2 的过期时间)。

非常感谢!

4

4 回答 4

0

第一个计时器到期阻止 io(主)线程运行,同时另一个线程将几个项目发布到 asio 工作队列,一旦计时器 1 的回调完成,第二个计时器到期处理,导致回调排队但未执行。由于“3”和“4”已经排队(而“1”阻塞了主线程),它们在“2”之前

asio的重点是不阻塞。通过将长时间运行的工作放在第一个计时器回调(睡眠)中,您阻止了 io 线程及时运行。您应该将该工作卸载到专用线程中,并将其完成发布回 asio。

于 2016-08-05T16:25:33.237 回答
0

您的第一个问题是您试图阻止处理程序内部:

timer1.expires_from_now(boost::posix_time::seconds(1));
timer1.async_wait([](const boost::system::error_code& error) {
    boost::this_thread::sleep(boost::posix_time::seconds(5)); // <--- HERE
    printf("1 ");
});

上面代码中发生的情况是,在timer1等待一秒钟后,它会将回调发布到io_service. 在io_service::run函数内部执行此回调,但此执行发生在主线程内部,因此它暂停五秒钟,防止timer2将其处理程序发布到io_service. 它会一直执行到程序执行的第六秒 (6 = 5+1)。

同时线程t被执行,并在程序执行的第五秒将这两个 printf("3") 和 printf("4") 发布到io_service.

boost::thread t([&io_service]() {
    boost::this_thread::sleep(boost::posix_time::seconds(5));
    io_service.post([]() {
        printf("3 ");
    });
    io_service.post([]() {
        printf("4 ");
    });
});

一旦处理程序timer1解除阻塞,它允许timer2将其处理程序发布到io_service. 这再次发生在程序执行的第六秒,也就是说,一旦printf("3")printf("4")已经发布!

总而言之,我相信您正在寻找的是:

#include "boost/asio.hpp"
#include "boost/thread.hpp"

int main()
{
    boost::asio::io_service io_service;
    boost::optional<boost::asio::io_service::work> work(io_service);

    boost::asio::deadline_timer timer1(io_service);
    boost::asio::deadline_timer timer2(io_service);

    timer1.expires_from_now(boost::posix_time::seconds(1));
    timer1.async_wait([](const boost::system::error_code& error) {
        printf("1 ");
    });

    timer2.expires_from_now(boost::posix_time::seconds(2));
    timer2.async_wait([](const boost::system::error_code& error) {
        printf("2 ");
    });

    boost::thread t([&io_service, &work]() {
        boost::this_thread::sleep(boost::posix_time::seconds(5));
        io_service.post([]() {
            printf("3 ");
        });
        io_service.post([&work]() {
            printf("4 ");
            work = boost::none;
        });
    });

    io_service.run();
    t.join();

    return 0;
}
于 2016-08-10T14:48:24.523 回答
0

io_service不保证处理程序的调用顺序。理论上,可以以任何顺序调用处理程序,其中一些排列不太可能发生。

如果需要以非常特定的顺序调用处理程序,则考虑以强制执行所需处理程序链的方式重构异步调用链。此外,人们可能会发现有必要使用strand提供的有保证的处理程序调用顺序。考虑不要尝试通过脆弱的睡眠和计时器来控制复杂的处理程序调用。

于 2016-08-07T05:45:28.007 回答
0

这实际上是一个相当复杂的例子。

io_service在主线程上运行。这是操作的顺序

主线:

  • 在 T0 + 1 请求计时器
  • 在 T0 + 2 请求计时器
  • 产生线程
  • 执行所有待处理的 io ( io_service.run())

次要线程:

  • 睡眠 5 秒
  • 请求计时器
  • 请求计时器

首先,在调用io_serviceuntil时不会执行任何io_service.run()操作。

一旦io_service.run()被调用,就会安排一个未来 1 秒的计时器。当该计时器触发时,它首先会休眠 5 秒,然后再打印 1。

当该线程正在执行时,辅助线程也会出现,并休眠 5 秒。该线程在处理程序中执行的计时器完成之前设置和调度timer1。由于这两个线程都休眠了 5 秒,因此 '2' 和 '3' 会立即发布到io_service.

现在事情变得有点棘手。似乎超时timer2现在应该已经过期(将来至少 5 秒),但是有两个命令直接发布到io_servicewhile 它正在处理timer1.

似乎在实现细节中,boost 优先于直接发布的操作而不是截止时间计时器操作。

于 2016-08-05T16:16:33.903 回答