【问题标题】:Boost.Asio: Why the timer is executed only once?Boost.Asio:为什么定时器只执行一次?
【发布时间】:2015-12-14 20:53:03
【问题描述】:

我有一个名为 read_packet 的函数。当没有连接请求或定时器发出信号时,此函数保持阻塞状态。

代码如下:

    std::size_t read_packet(const std::chrono::milliseconds& timeout,
                            boost::system::error_code& error)
    {
      // m_timer_ --> boost::asio::high_resolution_timer

      if(!m_is_first_time_) {
        m_is_first_time = true;

        // Set an expiry time relative to now.
        m_timer_.expires_from_now( timeout );
      } else {
        m_timer_.expires_at( m_timer_.expires_at() + timeout );
      }

      // Start an asynchronous wait.
      m_timer_.async_wait(
          [ this ](const boost::system::error_code& error){
            if(!error) m_is_timeout_signaled_ = true;
          }
      );

      auto result = m_io_service_.run_one();

      if( !m_is_timeout_signaled_ ) {
        m_timer_.cancel();
      }

      m_io_service_.reset();

      return result;
    }

该功能在未收到连接请求时正常工作。所有请求的接受都是异步的。

在接受连接后,run_one() 函数不会在定时器设置的时间内保持阻塞状态。该函数始终返回 1(已处理一个句柄)。这个句柄对应定时器。

我不明白为什么会出现这种情况。

为什么功能没有阻塞定时器所需的时间?

干杯。

注意:此函数在循环中使用。

更新

我有自己的io_service::run() 函数。此函数执行其他操作和任务。我想在一段时间内监听和处理网络级别:

  • 如果网络级别出现问题,io_service::run_one() 返回并且 read_packet() 将控制权返回给我的 run() 函数。

  • 否则,定时器被触发,read_packet() 将控制权返回给我的 run() 函数。

来自网络级别的所有内容都存储在数据结构中。然后我的 run() 函数对该数据结构进行操作。 它还运行其他选项。

    void run(duration timeout, boost::system::error_code& error)
    {
      time_point start = clock_type::now();
      time_point deadline = start + timeout;

        while( !stop() ) {
          read_packet(timeout, error);
          if(error) return;

          if(is_timeout_expired( start, deadline, timeout )) return;

          // processing network level

          // other actions
        }
    }

在我的情况下,套接字始终处于活动状态,直到客户端请求关闭连接。

在一个时间段内,您管理网络级别,而对于另一个时间段,您可以做其他事情。

【问题讨论】:

  • 你为什么运行run_one,而不是run?看起来您只等待一次,为什么您希望计时器运行多次?
  • @sehe :我有一个异步接受器。因此,io_service::run() 函数始终被阻止。在等待连接时,我想做其他任务。计时器允许我这样做。
  • 请看一些样本。看起来你在逆流划船。您可以同时使用线程来处理 IO 或发布任务来处理响应式任务吗?此外,这听起来完全矛盾。如果您同时使用计时器来回调您的wiel,那么运行不返回有什么问题?计时器仍会触发
  • 在我的代码中,我不能使用线程。问题是计时器没有触发。
  • 我认为你过于复杂了。这没有意义。让我告诉你:livecoding.tv/sehe

标签: c++ c++11 boost timer boost-asio


【解决方案1】:

在仔细阅读问题后,我了解到您实际上是在尝试使用 Asio 来获取同步 IO,但每次读取操作都会超时。

这不是 Asio 的初衷(因此得名“异步 IO 库”)。

当然,只要你坚持,你就可以做到。就像我说的,我觉得你把事情复杂化了。

在计时器的完成处理程序中,如果计时器已过期,只需取消套接字操作。 (请注意,如果没有,您将收到operation_aborted,因此请检查错误代码)。

独立的小例子(顺便说一句,这是你在寻求帮助时应该做的):

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>

struct Program {

    Program() { sock_.connect({ boost::asio::ip::address_v4{}, 6771 }); }

    std::size_t read_packet(const std::chrono::milliseconds &timeout, boost::system::error_code &error) {
        m_io_service_.reset();

        boost::asio::high_resolution_timer timer { m_io_service_, timeout };

        timer.async_wait([&](boost::system::error_code) { 
                sock_.cancel();
            });

        size_t transferred = 0;
        boost::asio::async_read(sock_, boost::asio::buffer(buffer_), [&](boost::system::error_code ec, size_t tx) {
                    error       = ec;
                    transferred = tx;
                });

        m_io_service_.run();

        return transferred;
    }

  private:
    boost::asio::io_service m_io_service_;
    using tcp = boost::asio::ip::tcp;

    tcp::socket sock_{ m_io_service_ };
    std::array<char, 512> buffer_;
};

int main() {
    Program client;
    boost::system::error_code ec;

    while (!ec) {
        client.read_packet(std::chrono::milliseconds(100), ec);
    }

    std::cout << "Exited with '" << ec.message() << "'\n"; // operation canceled in case of timeout
}

如果套接字操作成功,您可以看到例如:

Exited with 'End of file'

否则,如果操作未在 100 毫秒内完成,则会打印:

Exited with 'Operation canceled'

另见 await_operation 在这个先前的答案中,它更概括了这种模式:

【讨论】:

  • 如果您好奇我是如何阅读/回答过程的,请查看录制的直播:livecoding.tv/video/…
  • 很抱歉,我的英语很差,无法很好地解释。这不正是我想要的。我编辑了线程,添加了更多信息。
【解决方案2】:

好的,代码不正确。当定时器被取消时,定时器处理程序总是被执行。因此,io_service::run_one() 函数永远不会被阻塞。

更多信息:basic_waitable_timer::cancel

感谢您的帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 2022-11-20
    相关资源
    最近更新 更多