web-dev-qa-db-ja.com

asioのブースト-io_serviceの停止

私はboost :: asioを使用して、非常に基本的なUDPパケット収集を行っています。 io_serviceオブジェクトはワーカースレッドでインスタンス化され、io_service.run()はそのスレッド内から呼び出されます。私の問題は、パケットの収集が完了したときにio_service.run()を返すことです。

ワーカースレッドを停止するときに、他のスレッドからio_serviceのどのメソッドを呼び出すことができるかがわかりません。 io_serviceオブジェクトへの参照があり、別のスレッドから次の呼び出しを行います。

ios.dispatch( boost::bind( &udp_server::handle_kill, this ) );

私のudp_serverクラスでは、その関数のハンドラーは、単一のboost :: asio :: ip :: udp :: socketと単一のboost :: asio :: deadline_timerオブジェクトから保留中の作業をキャンセルします。どちらも、実行する保留中の非同期作業があります。その時点で、私はios.stop()を呼び出します。

void udp_server::handle_kill()
{
    m_socket.cancel();
    m_timer.cancel();
    m_ios.stop();
}

保留中の作業がないため、この時点でios.run()の呼び出しが返されるはずですが、これは発生しません。

では、なぜ戻らないのでしょうか。私にとって最も可能性の高い説明は、別のスレッドからio_service :: dispatch()を呼び出すべきではないということです。しかし、dispatch()メソッドは、まさにそれを行うために構築されたようです-io_service :: run()が機能しているスレッドで関数呼び出しをディスパッチします。そしてそれはまさにそれを行うようです。

したがって、これは私にいくつかの関連する質問を残します:

  1. Io_service :: dispatch()を正しく使用していますか?
  2. すべてのタスクがキャンセルされた場合、io_service :: run()が戻らない理由はありますか?
  3. socket :: upd :: cancel()は、ソケットを閉じてすべての作業を中止する正しい方法ではないようです。正しい方法は何ですか?

asioは私にとってはかなりうまく動作していますが、このアーキテクチャのビットをよりよく理解する必要があります。

その他のデータ

socket :: udp :: cancel()は、Win32で開いているソケットでサポートされていない操作であるようです。したがって、この操作は例外をスローして失敗します。これにより、実際にはio_service :: run()が終了しますが、絶対に望ましくありません。出口。

socket :: udp :: close()は、保留中のasync_receive_from()タスクをキャンセルしないようです。したがって、socket :: udp :: cancel()の代わりに呼び出すと、スレッドがio_service :: run()内のどこかに残るようです。

18
Mark Nelson

別のスレッドからio_service::stopを呼び出すのは安全です、これは 詳細に説明されています ドキュメントにあります

スレッドセーフ

個別のオブジェクト:安全です。

共有オブジェクト:未完成のrun()、run_one()、poll()またはpoll_one()があるときにreset()を呼び出すことを除いて、安全です呼び出しは未定義の動作になります。

あなたの質問へのコメントが示すように、あなたは本当にこれを再現可能な例に要約する必要があります。

24
Sam Miller