20

タイムアウトを使用して読み取る方法を知る必要があります (同期または非同期は関係ありません)。デバイスがシリアルポートに接続されているかどうかを確認したい。

そのために私は使用asio::writeし、デバイスの応答を待ちます。

デバイスが接続されている場合は正常に動作しますが、デバイスasio::read(serial, boost::asio::buffer(&r,1))がない場合はプログラムが停止するため、タイムアウトが必要です

が必要であることはわかっていますdeadline_timerが、関数でそれを使用する方法がわかりませんasync_read

それがどのように機能するかの例は本当に役に立ちます。

似たようなスレッドがたくさんあることは知っており、それらをたくさん読みましたが、問題を解決するのに役立つ解決策が見つかりません!

4

4 に答える 4

14

Igor R. によって投稿されたコードは、コンパイルされませんでした。これが彼のコードの改良版で、完全に機能します。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);
}
于 2014-07-29T15:15:12.947 に答える
7

むかしむかし、ライブラリの作成者は、タイムアウトを使用して同期的に読み取る次の方法を提案しました(この例には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); 
  } 
于 2012-10-30T17:49:31.653 に答える
6

deadline_timerでは使用しませんasync_read。ただし、次の 2 つの非同期プロセスを開始できます。

  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;
}
于 2012-10-30T16:36:42.523 に答える
0

非同期読み取りを行っても、コールバックが呼び出されることはなく、どこかに緩いスレッドがあるため、それ自体に1つまたは簡単な答えはありません。

それが可能な解決策の1つであると仮定するのは正しいですdeadline_timerが、それには多少の手間と共有状態が必要です。ブロッキング TCPの例がありますが、これは目的async_connectであり、何もすることがないときに返されるというクールな点があります。read最悪のシナリオはそうしません-無効なリソースが原因でクラッシュして書き込みます。これはdeadline timer選択肢の 1 つですが、実際には次のような簡単な方法があります。

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

基本的に、別のスレッドで読み取りを行い、タイムアウトした場合は強制終了します。Boost.Threadsを読む必要があります。

中断する場合は、リソースがすべて閉じられていることを確認してください。

于 2012-10-30T16:35:46.137 に答える