Unusual output for tutorial in boost asio doc

In the fifth tutorial, in which the code I gave at the bottom of the question, there was an output in the asio documentation:

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

      

After the first, it looks like expected, with consistency. But even though Timer1 is chained first, why does Timer 2 start up 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;
}

      

0


source to share


1 answer


A is strand

used to ensure sequential execution of handlers. In addition, under certain conditions, it provides a guarantee on the handling order of handlers dispatched or dispatched across a strand. The example does not meet these conditions. In addition, there is no guarantee that an interleaving pattern will be observed between completion handlers.


IO objects like timers are not threaded, completion handlers. A strand

can be considered associated with a queue of FIFO handlers. If there are no handlers in the handler queue that are currently placed in io_service

, then it will pull one handler out of itself and place it in the associated one io_service

. This thread ensures that handlers posted on the same chain will not be called at the same time.

Completion handlers provided a

and b

, if a

queued before b

, then a

before will be called b

. This is the basic guarantee that all scenarios can be reduced. The scenarios that are a

guaranteed to be b

documented as follows:



  • strand.post(a)

    happens before strand.post(b)

    . Since it post()

    does not attempt to invoke the provided handler internally post()

    , it a

    is queued before b

    .
  • strand.post(a)

    occurs before strand.dispatch(b)

    where it strand.dispatch(b)

    runs outside of the line. Because it strand.dispatch(b)

    happens outside of the line, it b

    is enqueued as if on post()

    . So it is shortened to strand.post(a)

    prior strand.post(b)

    .
  • strand.dispatch(a)

    happens before strand.post(b)

    , where strand.dispatch(a)

    happens outside of the line. Because it strand.dispatch(a)

    happens outside of the line, it a

    is queued as if on post()

    . So it is shortened to strand.post(a)

    prior strand.post(b)

    .
  • strand.dispatch(a)

    happens before strand.dispatch(b)

    where both are executed outside of the line. Since neither of these occurs inside the thread, both handlers are queued as if they were post()

    . So it is shortened to strand.post(a)

    prior strand.post(b)

    .

io_service

makes no guarantees as to the order in which the processors are contacted. Also, the handler returned from strand.wrap()

does not run in the context of a thread. Sample code simplifies:

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

      

If the operations async_wait

complete at the same time, the completion handlers wrapped_print1

and wrapped_print2

will be sent in io_service

for the pending call. Since it io_service

does not make any guarantees in the order of handling, it can call first wrapped_print1

or it can call first wrapped_print2

. Both handlers are wrapped_print

called outside of the string context in an unspecified order, resulting in print1()

and print2()

chained in an unspecified order.

The indeterminate order in which it is called wrapped_print

is that the original example is not guaranteed to be able to observe the alternating pattern between the handlers print1

and print2

. However, given the current implementation of the internal scheduler io_service

, you will see a pattern like this.

+1


source







All Articles