【问题标题】:use io_service::post (boost) when deadline_timer is waiting当 deadtime_timer 等待时使用 io_service::post (boost)
【发布时间】:2016-12-12 01:46:33
【问题描述】:

我在使用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 的过期时间)。

非常感谢!

【问题讨论】:

    标签: c++ boost timer boost-asio deadlines


    【解决方案1】:

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

    io_service 将在主线程上运行。以下是操作顺序

    主线程:

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

    次要线程:

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

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

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • ⚠ 无需重新设计调用链或修改计时器,一个非常脆弱且严重依赖于实现细节的解决方案可用here ⚠
        【解决方案4】:

        您的第一个问题是您试图在处理程序中进行阻塞:

        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;
        }
        

        【讨论】:

          猜你喜欢
          • 2012-11-26
          • 2020-05-27
          • 1970-01-01
          • 2011-09-16
          • 1970-01-01
          • 2019-01-22
          • 1970-01-01
          • 2016-02-27
          • 2021-02-22
          相关资源
          最近更新 更多