【问题标题】:asio::read with timeoutasio::read with timeout
【发布时间】:2012-10-19 01:42:45
【问题描述】:

我需要知道如何读取超时(同步或异步无关紧要)。我想检查设备是否与串行端口连接。

为此,我使用asio::write,然后等待设备的响应。

如果设备已连接asio::read(serial, boost::asio::buffer(&r,1)) 工作正常,但如果没有设备,程序将停止,这就是我需要超时的原因

我知道我需要一个deadline_timer,但我不知道如何在async_read 函数中使用它。

举例说明它的工作原理会很有帮助。

我知道有很多类似的主题,我阅读了很多,但我找不到可以帮助我解决问题的解决方案!

【问题讨论】:

    标签: c++ boost timeout serial-port boost-asio


    【解决方案1】:

    code posted by Igor R. 没有为我编译。这是我改进的他的代码版本,效果很好。它使用 lambdas 来摆脱 set_result 辅助函数。

    template <typename SyncReadStream, typename MutableBufferSequence>
    void readWithTimeout(SyncReadStream& s, const MutableBufferSequence& buffers, const boost::asio::deadline_timer::duration_type& expiry_time)
    {
        boost::optional<boost::system::error_code> timer_result;
        boost::asio::deadline_timer timer(s.get_io_service());
        timer.expires_from_now(expiry_time);
        timer.async_wait([&timer_result] (const boost::system::error_code& error) { timer_result.reset(error); });
    
        boost::optional<boost::system::error_code> read_result;
        boost::asio::async_read(s, buffers, [&read_result] (const boost::system::error_code& error, size_t) { read_result.reset(error); });
    
        s.get_io_service().reset();
        while (s.get_io_service().run_one())
        { 
            if (read_result)
                timer.cancel();
            else if (timer_result)
                s.cancel();
        }
    
        if (*read_result)
            throw boost::system::system_error(*read_result);
    }
    

    【讨论】:

    • 这非常适合模拟boost::asio::async_read 的超时。真正简单而可靠的解决问题的方法。我喜欢这种简单的技术,我想将它用于boost::asio::async_write 案例?但它似乎不适用于写案例。你介意看看this question here 吗?
    • This example 类似,但不使用 lambda,也没有模板化。
    • 对于超时读取,我们仍然没有任何解决方案?
    • s.get_io_service().reset()s.get_io_service().run_one() 不起作用,因为 io_service 已被 io_context 取代,但我仍然不知道如何调整代码。有什么想法吗?
    • 我已经在我的代码中成功实现了上述解决方案,但是正如@Tom Quarendon in @Igor R. answer 所述,我需要在取消计时器后添加一个break(Boost 版本:1.69. 0)
    【解决方案2】:

    曾几何时,库作者提出如下方式to read synchronously with timeout(本例涉及tcp::socket,但可以使用串口代替):

      void set_result(optional<error_code>* a, error_code b) 
      { 
        a->reset(b); 
      } 
    
    
      template <typename MutableBufferSequence> 
      void read_with_timeout(tcp::socket& sock, 
          const MutableBufferSequence& buffers) 
      { 
        optional<error_code> timer_result; 
        deadline_timer timer(sock.io_service()); 
        timer.expires_from_now(seconds(1)); 
        timer.async_wait(boost::bind(set_result, &timer_result, _1)); 
    
    
        optional<error_code> read_result; 
        async_read(sock, buffers, 
            boost::bind(set_result, &read_result, _1)); 
    
        sock.io_service().reset(); 
        while (sock.io_service().run_one()) 
        { 
          if (read_result) 
            timer.cancel(); 
          else if (timer_result) 
            sock.cancel(); 
        } 
    
    
        if (*read_result) 
          throw system_error(*read_result); 
      } 
    

    【讨论】:

    • 我不懂技术。当没有其他可能正在进行的异步操作时,这可能适用于单线程场景。是什么让while循环终止?如果周围有其他线程,调用 io_service.reset() 是否安全?
    • @Tom Quarendon 我想你是对的,如果多个线程正在运行 io_service,它将无法工作。但是请注意,可以使用 io_service-per-core 模型,其中每个 io_service 有一个线程。
    • @Iror R。在测试多线程服务器上进行了尝试,它似乎可以工作。我不得不在“if”和“else if”中添加一个“break”,以使循环终止。我不知道这是否“应该”在这个多线程环境中工作
    • 实际上,擦洗它。我无法获得在所有情况下都可以正常工作的版本,单线程和多线程,以及其他流量是否持续。
    【解决方案3】:

    您不要在async_read 中使用deadline_timer。但是你可以启动两个异步进程:

    1. 串行端口上的async_read 进程。 boost::asio::serial_port 有一个 cancel 方法,可以取消对它的所有异步操作。
    2. 具有所需超时的截止时间计时器。在deadline_timer 的完成处理程序中,您可以cancel 串行端口。这应该会关闭 async_read 操作并调用其完成处理程序并出现错误。

    代码:

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>
    #include <boost/array.hpp>
    
    class timed_connection
    {
        public:
            timed_connection( int timeout ) :
                timer_( io_service_, boost::posix_time::seconds( timeout ) ),
                serial_port_( io_service_ )
            {
            }
    
            void start()
            {
                  timer_.async_wait
                    (
                     boost::bind
                     (
                      &timed_connection::stop, this
                     )
                    );
    
                // Connect socket
                // Write to socket
    
                // async read from serial port
                boost::asio::async_read
                    (
                     serial_port_, boost::asio::buffer( buffer_ ),
                     boost::bind
                     (
                      &timed_connection::handle_read, this,
                      boost::asio::placeholders::error
                     )
                    );
    
                io_service_.run();
            }
    
        private:
            void stop()
            {  
                serial_port_.cancel();
            }
    
            void handle_read ( const boost::system::error_code& ec)
            {  
                if( ec )
                {  
                    // handle error
                }
                else
                {  
                    // do something
                }
            }
    
        private:
            boost::asio::io_service io_service_;
            boost::asio::deadline_timer timer_;
            boost::asio::serial_port serial_port_;
            boost::array< char, 8192 > buffer_;
    };
    
    int main()
    {
        timed_connection conn( 5 );
        conn.start();
    
        return 0;
    }
    

    【讨论】:

    • 问题明确表明他使用的是串行设备,而不是 TCP 端点。如果他使用 *nix,UNIX 套接字可能是一个选项,但没有说明操作系统,它们有点破坏可移植性。
    • @TC1,当然,编辑了代码。从概念上讲,Asio 对其进行抽象的方式,没有任何改变。
    【解决方案4】:

    本身没有一个或 简单 的答案,因为即使您执行异步读取,回调也永远不会被调用,并且您现在在某处有一个松散的线程。

    您认为deadline_timer 是可能的解决方案之一是正确的,但它需要一些摆弄和共享状态。有blocking TCP example,但那是给async_connect 的,当它无事可做时,它返回是一件很酷的事情。 read 不会那样做,最坏的情况——它会因为无效资源而崩溃和烧毁。所以deadline timer 是你的选择之一,但实际上有一个更简单的,有点像这样:

    boost::thread *newthread = new boost::thread(boost::bind(&::try_read));
    if (!newthread->timed_join(boost::posix_time::seconds(5))) {
        newthread->interrupt();
    }
    

    基本上,在另一个线程中读取并在超时时将其终止。你应该阅读Boost.Threads

    如果你打断它,请确保资源全部关闭。

    【讨论】:

    • 你确定asio i/o操作是中断点吗?
    • 有人能证实这一点吗?这个解决方案有效吗?
    • 我已经对此进行了测试,但对我不起作用。在 asio i/o 操作期间,interrupt 不会停止线程。
    • 对我也不起作用(用boost::thread::timed_join() 测试:线程没有停止)。
    猜你喜欢
    • 2023-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-14
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多