首页 > 解决方案 > Is running new thread in Timer.5 boost::asio tutorial necessary?

问题描述

Question 1: Is starting a new thread in Timer.5 boost::asio tutorial really needed?

  ...
  boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
  io.run();
  t.join();
  ...

Tutorial says:

As you already know, the asio library provides a guarantee that callback handlers will only be called from threads that are currently calling io_context::run().

But if I run this

  ...
  // boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
  io.run();
  // t.join();
  ...

... callbacks are still executed despite the fact that timers are started asynchronously.

Also I may have understood this tutorial in wrong way, I am new at boost::asio but it looks to me that this tutor doesn't depict what author wanted to demonstrate.

Author says:

The single threaded approach is usually the best place to start when developing applications using asio. The downside is the limitations it places on programs, particularly servers, including:

  • Poor responsiveness when handlers can take a long time to complete.
  • An inability to scale on multiprocessor systems.

So when author tells about poor responsiveness it means that we could run multiple tasks (callbacks in this case) concurrently so that we engage all available cores. But this approach works ideally when there are no shared data to modify. However in this tutorial callbacks DO modify shared variable count and write to cout. To avoid concurrent modification author places callbacks into strand object which guarantees that callbacks execute one after another in order they came into the "queue".

So callbacks are not something heavy and long-to-execute in this tutorial ...

Question 2: Do you think that using strand object is necessary in this tutorial?

I think this tutorial is overengineered, once again please correct me if I am wrong, but this code without additional thread and strand object seems to work the same way as original tutorial:

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

class printer
{
public:
  printer(boost::asio::io_context& io)
    : timer1_(io, boost::asio::chrono::seconds(1)),
      timer2_(io, boost::asio::chrono::seconds(1)),
      count_(0)
  {
    timer1_.async_wait(boost::bind(&printer::print1, this));

    timer2_.async_wait(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_.expiry() + boost::asio::chrono::seconds(1));
      timer1_.async_wait(boost::bind(&printer::print1, this));
    }
  }

  void print2()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 2: " << count_ << std::endl;
      ++count_;

      timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1));
      timer2_.async_wait(boost::bind(&printer::print2, this));
    }
  }

private:
  boost::asio::steady_timer timer1_;
  boost::asio::steady_timer timer2_;
  int count_;
};

int main()
{
  boost::asio::io_context io;
  printer p(io);
  io.run();

  return 0;
}

Question 3: Is the code above correct?

Thanks in advance.

标签: c++boostboost-asio

解决方案


从第二个线程调用run()a 是本教程的重点。

你的两个问题有相同的答案。Timer.5 教程向您展示了如何让超过 1 个线程服务io_context(超过 1 个线程调用run())。但是运行多线程意味着您必须保护共享状态免受并发访问,这就是strand需要对象的原因。

run()你是对的,在这个例子中,如果你只从一个线程调用,你就不需要 strand 。


推荐阅读