boost asio doc中教程的奇怪输出

Posted

技术标签:

【中文标题】boost asio doc中教程的奇怪输出【英文标题】:Weird output for a tutorial in boost asio doc 【发布时间】:2015-01-18 14:36:54 【问题描述】:

在第5个教程中,我在问题底部给出的代码,asio文档介绍了输出如下:

Timer 2: 0
Timer 1: 1
Timer 2: 2
Timer 1: 3
Timer 2: 4
.
.
.

在第一个之后,它是预期的,具有顺序。 但是即使 Timer1 先被包裹在 strand 中,为什么 Timer 2 会先开始运行呢?

#include <iostream>
#include <asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer

    public:
        printer(asio::io_service& io)
            : strand_(io),
            timer1_(io, boost::posix_time::seconds(1)),
            timer2_(io, boost::posix_time::seconds(1)),
            count_(0)
        
            timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
            timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
        

        ~printer()
        
            std::cout << "Final count is " << count_ << "\n";
        

        void print1()
        
            if (count_ < 10)
            
                std::cout << "Timer 1: " << count_ << "\n";
                ++count_;
                timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
                timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
            
        
        void print2()
        
            if (count_ < 10)
            
                std::cout << "Timer 2: " << count_ << "\n";
                ++count_;
                timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
                timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
            
        
private:
    asio::strand strand_;
    asio::deadline_timer timer1_;
    asio::deadline_timer timer2_;
    int count_;
;

int main()

    asio::io_service io;
    printer p(io);
    asio::thread t(boost::bind(&asio::io_service::run, &io));
    io.run();
    t.join();
    system("PAUSE");
    return 0;

【问题讨论】:

嗯,有没有可能是我没弄明白? strand 是否真的介意包装的顺序,还是只是确保这些计时器不会同时运行? 大胆猜测:从main 中删除io.run();线程t 已经调用它。这看起来像是一种竞争条件,可能会使有趣的事情成为可能。 这就是本教程的重点。它展示了 strand 如何使从多个线程运行 io.run() 成为可能。当然,当你删除线程 t 时,一切正常。 【参考方案1】:

strand 用于提供处理程序的串行执行。此外,在某些条件下,它为通过链发布或调度的处理程序的调用顺序提供保证。该示例不满足这些条件。此外,不能保证会观察到完成处理程序之间的交替模式。


IO 对象,例如计时器,不被链包裹,完成处理程序是。 strand 可以被认为与处理程序的 FIFO 队列相关联。如果处理程序队列当前没有处理程序发布到io_service,那么它将从自身弹出一个处理程序并将其发布到关联的io_service。此流程保证不会同时调用发布到同一 strand 中的处理程序。

strand.post() 将处理程序排入链中。 如果当前调用者在链的上下文中运行,strand.dispatch() 将运行处理程序。否则,它将像 post() 一样将处理程序排入队列。 strand.wrap() 返回一个新的完成处理程序,当被调用时,将 dispatch() 包装的处理程序放入链中。从本质上讲,wrap() 将处理程序的分派延迟到链中。

给定完成处理程序ab,如果ab 之前入队,那么a 将在b 之前被调用。这是所有场景都可以减少的根本保证。在b之前保证a的场景是documented,如下:

strand.post(a) 发生在 strand.post(b) 之前。由于post() 不会尝试在post() 中调用提供的处理程序,因此ab 之前排队。 strand.post(a) 发生在 strand.dispatch(b) 之前,其中 strand.dispatch(b) 在链之外执行。当strand.dispatch(b) 出现在链之外时,b 就像post() 一样排队。因此,这减少到 strand.post(a) 发生在 strand.post(b) 之前。 strand.dispatch(a) 发生在 strand.post(b) 之前,其中 strand.dispatch(a) 出现在链之外。由于strand.dispatch(a) 出现在链之外,a 就像post() 一样排队。因此,这减少到 strand.post(a) 发生在 strand.post(b) 之前。 strand.dispatch(a) 发生在 strand.dispatch(b) 之前,两者都在链外执行。由于两者都没有出现在一个链中,所以两个处理程序都像 post() 一样排队。因此,这减少到 strand.post(a) 发生在 strand.post(b) 之前。

io_servicemakes no guarantees 关于处理程序的调用顺序。此外,从strand.wrap() 返回的处理程序不在链的上下文中运行。示例代码简化为:

auto wrapped_print1 = strand.wrap(&print1);
auto wrapped_print2 = strand.wrap(&print2);
timer1_.async_wait(wrapped_print1);
timer2_.async_wait(wrapped_print2);

如果async_wait 操作同时完成,则wrapped_print1wrapped_print2 完成处理程序将发布到io_service 以进行延迟调用。由于io_service不保证调用顺序,它可以选择先调用wrapped_print1,也可以选择先调用wrapped_print2。两个wrapped_print 处理程序都在链的上下文之外以未指定的顺序被调用,导致print1()print2() 以未指定的顺序被排入链中。

wrapped_print 调用的未指定顺序是为什么不能保证在原始示例中观察到 print1print2 处理程序之间的交替模式。然而,鉴于io_service 的内部调度程序的当前实现,人们会观察到这样一种模式。

【讨论】:

warp的存在是什么意思? warppostdispatch 看起来很相似,但 warp 并不能确保处理程序的执行顺序。你能解释一下within the context of the strand是什么(任何调用strand.post的函数都有临时变量的作用域,那么outside the context of the strand是什么?)和outside the strand? @Tanner Sansbury

以上是关于boost asio doc中教程的奇怪输出的主要内容,如果未能解决你的问题,请参考以下文章

Boost Asio教程集合

C++ Boost Asio 简单聊天教程

boost asio tcp 收发教程

boost::asio 与 boost::unique_future

使用 Fiddler 拦截 Boost.Asio HTTP/S 请求

boost::asio::async_accept 处理程序未被调用