asioを使用して非ブロック読み取りを実行するにはどうすればよいですか?
-
19-08-2019 - |
質問
boost :: asioを使用して、シリアルポート上のデバイスの読み取りと書き込みを試みています。 boost :: asio:read()とboost :: asio :: serial_port :: read_some()の両方は、読み取るものがない場合にブロックします。代わりに、この状態を検出し、デバイスをキックスタートするコマンドをポートに書き込みたいと思います。
利用可能なデータがないことを検出するにはどうすればよいですか
必要に応じて非同期ですべてを実行できる場合は、可能であれば余分な複雑さを避けたいだけです。
解決
実際にはいくつかのオプションがあります。シリアルポートの組み込みasync_read_some
関数を使用するか、スタンドアロン関数boost::asio::async_read
(またはdeadline_timer
)を使用できます。
あなたは事実上<!> quot; blocked <!> quot;の状況に陥ります、なぜならこれらはどちらも(1)データが読み込まれないか(2)エラーが発生しない限りコールバックを呼び出さないからです。これを回避するには、read_some
オブジェクトを使用してタイムアウトを設定します。最初にタイムアウトが発生した場合、利用可能なデータはありませんでした。それ以外の場合は、データを読み取ります。
追加された複雑さはそれほど悪くありません。同様の動作を持つ2つのコールバックが作成されます。 <!> quot; read <!> quot;または<!> quot; timeout <!> quot;コールバックはエラーで起動しますが、それはレース敗者です。どちらかがエラーなしで発動した場合、それがレースの勝者であることがわかります(もう一方の呼び出しをキャンセルする必要があります)。 io_svc.run()
をブロックする呼び出しがあった場所で、run
を呼び出します。関数はrun()
を呼び出すときに以前と同様にブロックしますが、今回は期間を制御します。
例を次に示します。
void foo()
{
io_service io_svc;
serial_port ser_port(io_svc, "your string here");
deadline_timer timeout(io_svc);
unsigned char my_buffer[1];
bool data_available = false;
ser_port.async_read_some(boost::asio::buffer(my_buffer),
boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>));
timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),
boost::asio::placeholders::error));
io_svc.run(); // will block until async callbacks are finished
if (!data_available)
{
kick_start_the_device();
}
}
void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error || !bytes_transferred)
{
// No data was read!
data_available = false;
return;
}
timeout.cancel(); // will cause wait_callback to fire with an error
data_available = true;
}
void wait_callback(serial_port& ser_port, const boost::system::error_code& error)
{
if (error)
{
// Data was read and this timeout was canceled
return;
}
ser_port.cancel(); // will cause read_callback to fire with an error
}
これにより、特定のニーズに合わせて、あちこちでいくつかの調整を行うだけで開始できます。これがお役に立てば幸いです!
別の注意:コールバックを処理するために余分なスレッドは必要ありませんでした。すべては<=>の呼び出し内で処理されます。すでにこれに気付いていたかどうかわかりません...
他のヒント
実際には、ここでの回答が暗示しているよりもはるかに単純であり、同期して実行できます。
ブロッキング読み取りが次のようなものであったと仮定します:
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
次にそれを置き換えます
socket.non_blocking(true);
size_t len = 0;
error = boost::asio::error::would_block;
while (error == boost::asio::error::would_block)
//do other things here like go and make coffee
len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error);
std::cout.write(recv_buf.data(), len);
ほとんどすべての送信/受信メソッドにある、オーバーロードされた代替形式のreceive_fromを使用します。残念ながらflags引数を取りますが、0はうまく機能しているようです。
自由関数asio :: async_readを使用する必要があります。