2

I have the following program, taken from http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/tutorial/tuttimer5.html

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


class Printer {
private:
  boost::asio::io_service::strand strand_;
  boost::asio::deadline_timer timer1_;
  boost::asio::deadline_timer timer2_;
  int count_;

public:
  Printer(boost::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_ << std::endl;
  }

  void print1() {
    if(count_ < 10) {
      std::cout << "Timer 1: " << count_ << std::endl;
      ++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_ << std::endl;
      ++count_;

      timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
      timer2_.async_wait(strand_.wrap(boost::bind(&Printer::print2, this)));
    }
  }
};

int main() {
  boost::asio::io_service io;
  Printer p(io);
  boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
  io.run();
  t.join();

  return 0;
}

As far as I understand,

timer1_.async_wait(strand_.wrap(boost::bind(&Printer::print1, this)));
timer2_.async_wait(strand_.wrap(boost::bind(&Printer::print2, this)));

ensures that print1and print2 will never be run concurrently. However, what I don't understand is how things are settled in the main function.

I assume that

boost::thread t(boost::bind(&boost::asio::io_service::run, &io));

creates a new thread in addition to the main one io.run() and that t.join() blocks the main thread until t completes.

What I don't understand is, what work is assigned to thread t and what work is assigned to the main thread. Moreoever, what's the point of having two async_wait in two different threads?

Thanks,

Pierre P.
  • 1,055
  • 2
  • 14
  • 26

2 Answers2

3

The assignment of work is not really obvious in a real world scenario. You can call boost::asio::io_service::run() from multiple threads, and those threads will share the workload. Per the documentation, "All threads that are waiting in the pool are equivalent and the io_service may choose any one of them to invoke a handler."

Having the async waits on two different threads in this case is more of an exercise to show the mechanics of how it works, rather than having a well defined use case.

Chad
  • 18,706
  • 4
  • 46
  • 63
  • Thanks for your answer. What I don't understand is `strand` ensure that will never run concurrently. Hence, what's the point of having two threads running these? – Pierre P. Nov 07 '17 at 20:21
  • The strand ensures they will not run concurrently, regardless of which thread actually runs the handlers. It's possible that each handler could be run on its own thread, but in any case the handlers will execute in the order they were posted. – Chad Nov 07 '17 at 21:40
0

What I don't understand is strand ensure that will never run concurrently. Hence, what's the point of having two threads running these?

See Why do I need strand per connection when using boost::asio? A single thread would be an implicit strand, yes.

You use more threads if a single thread is not good enough for your needs.

The timers serve different purposes, it is not one per thread. (There's no concept of thread affinity in io_service tasks. Strands are a logical concept without thread affinity, unless you force the implicit strand like mentioned)

sehe
  • 374,641
  • 47
  • 450
  • 633