Weird output for a tutorial in boost asio doc

有些话、适合烂在心里 提交于 2019-12-13 21:04:38

问题


In the 5th tutorial, of which the code I have given at bottom of the question, asio documentation introduced the output comes as follows :

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

After the first one it is as expectable, with the sequence. But even though Timer1 is wrapped in the strand first, why does Timer 2 starts running first ?

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

回答1:


A strand is used to provide serial execution of handlers. Also, under certain conditions, it provides a guarantee on the order of invocation of handlers posted or dispatched through the strand. The example does not meet these conditions. Furthermore, there is no guarantee that one will observe the alternating pattern between the completion handlers.


IO Objects, such as timers, are not wrapped by strands, completion handlers are. A strand can be thought of as being associated with a FIFO queue of handlers. If a handler queue has no handlers currently posted into an io_service, then it will pop one handler from itself and post it into the associated io_service. This flow guarantees that handlers posted into the same strand will not be invoked concurrently.

  • strand.post() enqueues a handler into the strand.
  • strand.dispatch() will run the handler if the current caller is running within the context of the strand. Otherwise, it will enqueue the handler as if by post().
  • strand.wrap() return a new completion handler that, when invoked, will dispatch() the wrapped handler into the strand. Essentially, wrap() defers the dispatching of a handler into the strand.

Given completion handlers a and b, if a is enqueued before b, then a will be invoked before b. This is the fundamental guarantee to which all scenarios can be reduced. The scenarios in which a is guaranteed before b are documented as followed:

  • strand.post(a) happens before strand.post(b). As post() does not attempt to invoke the provided handler within post(), a is enqueued before b.
  • strand.post(a) happens before strand.dispatch(b), where strand.dispatch(b) is performed outside of a strand. As strand.dispatch(b) occurs outside of a strand, b is queued as if by post(). Thus, this reduces down to strand.post(a) happening before strand.post(b).
  • strand.dispatch(a) happens before strand.post(b), where strand.dispatch(a) occurs outside of a strand. As strand.dispatch(a) occurs outside of a strand, a is queued as if by post(). Thus, this reduces down to strand.post(a) happening before strand.post(b).
  • strand.dispatch(a) happens before strand.dispatch(b), where both are performed outside of the strand. As neither occur within a strand, both handlers are enqueued as if by post(). Thus, this reduces down to strand.post(a) happening before strand.post(b).

The io_service makes no guarantees about the invocation order of handlers. Additionally, the handler returned from strand.wrap() does not run within the context of a strand. The example code simplifies to:

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

If the async_wait operations complete at the same time, the wrapped_print1 and wrapped_print2 completion handlers will be posted into the io_service for deferred invocation. As the io_service makes no guarantees on the invocation order, it may choose to invoke wrapped_print1 first, or it may choose to invoke wrapped_print2 first. Both wrapped_print handlers are being invoked outside of the context of the strand in an unspecified order, resulting in print1() and print2() being enqueued into the strand in an unspecified order.

The unspecified order in which wrapped_print are invoked is why one is not guaranteed to observe an alternating pattern between the print1 and print2 handlers in the original example. However, given the current implementation of the io_service's internal scheduler, one will observe such a pattern.



来源:https://stackoverflow.com/questions/28010880/weird-output-for-a-tutorial-in-boost-asio-doc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!