阿西奥:为什么计时器只执行一次

Boost.Asio: Why the timer is executed only once?

本文关键字:一次 执行 为什么 计时器      更新时间:2023-10-16

我有一个名为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
        }
    }

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

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

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

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

但当然,如果你坚持,你可以做到。就像我说的,我觉得你把事情搞得太复杂了。

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

小的自我包含的例子(顺便说一句,这是你在寻求帮助时应该做的事情):

在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,它进一步概括了这种模式:

  • boost::asio+std::future-关闭套接字后发生访问冲突

好的,代码不正确。当计时器被取消时,计时器处理程序总是被执行。因此,io_service::run_one()功能从未被阻止。

更多信息:basic_waitable_timer::取消

谢谢你的帮助。