ブロックされたBoost :: Threadをキルする
-
07-07-2019 - |
質問
2つの istream
からの入力をブロックするアプリケーションを作成しています。
いずれかの istream
からの読み取りは同期(ブロック)呼び出しなので、読み取りを行うために2つの Boost :: thread
を作成することにしました。
これらのスレッドのいずれかが「終了」に到達できます。 (受け取った入力に基づいて)、および「終了」に達すると、両方の入力ストリームの受信が停止します。残念ながら、どちらがそうなのかわかりません。
したがって、両方のスレッドで join()
を実行することはできません。実際に戻る(ブロック解除する)スレッドは1つだけなので(どちらを事前に決定することもできません)。
もう一方を強制的に終了させる必要がありますが、入力を待機しているのでブロックされているため、それ自体が戻るべきタイミングを判断できません(条件変数かどうか)。
次のいずれかの方法です:
- 信号にboost :: threadを送信する、または
-
istream
を強制的に"失敗"させるか、 - Kill a Boost :: thread?
注:
-
istreams
の1つはcin
です
- プロセスを再起動しようとしているので、リセットを禁止する方法で入力ストリームを閉じることはできません。
編集:
- "終了"に到達し、どのスレッドが正常に終了し、どのスレッドを強制終了する必要があるかがわかります。それは私が把握する必要のある殺害(またはistreamから読み取るための別の戦略)。
- 適切に終了してクリーンアップするには両方のスレッドが必要です:(
ありがとう!
解決
クロスプラットフォームでそれを行う方法はないと思いますが、pthread_cancelはあなたが探しているものでなければなりません。ブーストスレッドを使用すると、 native_handle を呼び出し、そのスレッドでpthread_cancelを呼び出します。
さらに良い方法は、ブーストを使用することです。 asio は、複数のファイルに対するselect呼び出しに相当します。この方法では、1つのスレッドが入力を待機してブロックされますが、どちらの入力ストリームからでも発生する可能性があります。ただし、iostreamを使用してこのようなことを行うのがどれほど簡単かはわかりません。
他のヒント
はい!
boost :: thread :: terminate()
は、仕様どおりに機能します。
ターゲットスレッドが例外をスローするようにします。キャッチされないと仮定すると、スタックは適切に巻き戻され、すべてのリソースが破壊され、スレッドの実行が終了します。
終了は即時ではありません。 (とにかく、その時点で間違ったスレッドが実行されています。)
事前に定義された条件の下で発生します。おそらく最も便利なのは、 boost :: this_thread :: sleep();
を呼び出すときです。
ブーストスレッドがI / O操作でブロックしている場合(たとえば、 cin>> whatever
)、 boost :: thread :: terminate()
は強制終了しませんスレッド。 cin
i / oは有効な終端地点ではありません。 22.をキャッチ。
まあ、Linuxでは、ブロッキングIOを中断するため、pthread_signal(SIGUSR1)を使用します。私のコードを移植するときに発見したようなウィンドウ上の呼び出しはありません。ソケット読み取り呼び出しで非推奨のもののみ。 Windowsでは、ブロッキングコールを中断するイベントを明示的に定義する必要があります。そのため、ブロッキングIOを中断する一般的な方法としては(AFAIK)はありません。
boost.thread設計は、明確に識別された割り込みポイントを管理することでこれを処理します。 boost.asioがよくわからないので、とにかくそれを当てにしたくないようです。ノンブロッキングパラダイムを使用するようにリファクタリングしたくない場合は、ノンブロッキング(ポーリング)とブロッキングIOの間に何かを使用します。それは(擬似コード?)のようなことをする:
while(!stopped && !interrupted)
{
io.blockingCall(timeout);
if(!stopped && !interrupted)
{
doSomething();
}
}
次に、2つのスレッドを中断して結合します...
おそらくあなたの場合は簡単ですか? 1つのスレッドが終了したことを知っているマスタースレッドがある場合は、他のスレッドのIOを閉じるだけですか?
編集: ところで、私はあなたが持っている最終的な解決策に興味があります...
私自身も同様の問題を抱えており、この解決策に到達しました。この質問の他の読者は役に立つかもしれません:
wait()コマンドで条件変数を使用していると仮定すると、Boostでは、wait()ステートメントが自然な割り込みポイントであることを知っておくことが重要です。したがって、waitステートメントを使用してコードの周りにtry / catchブロックを配置し、関数がcatchブロックで正常に終了できるようにします。
ここで、スレッドポインターを含むコンテナがあると仮定して、スレッドポインターを反復処理し、各スレッドでinterrupt()を呼び出し、続いてjoin()を呼び出します。
これですべてのスレッドが正常に終了し、Boost関連のメモリクリーンアップはすべて正常に動作するはずです。
スレッドを強制終了するのではなく、代わりにいつでもスレッドに参加できます。失敗した場合は、代わりに他のスレッドに参加します。 (2つのスレッドの少なくとも1つに常に参加できると仮定します。)
boost:threadでは、 timed_join 関数。
ただし、正しい答えを確認したい場合は、時間制限のある待機で非ブロッキングioを使用します。非同期ioのノンブロッキングで、同期ioのフロー構造を取得できます。
istreamからの読み取りについて説明しますが、istreamはインターフェイスにすぎません。 stdinの場合は、stdinファイル記述子をfcloseして読み取りを中断できます。もう一方については、どこから読んでいるかに依存します...
スレッドは、単純な方法であなたが望むことをするのを助けていないようです。 Boost.Asioがお好みでない場合は、 select()
の使用を検討してください。
アイデアは、2つのファイル記述子を取得し、 select()
を使用して、どちらに入力があるかを通知することです。 cin
のファイル記述子は通常 STDIN_FILENO
です。もう1つを取得する方法は仕様によって異なります(ファイルの場合は、 ifstream
を使用する代わりに open()
します)。
ループで select()
を呼び出して、読み込む入力を見つけ、停止したい場合は、ループから抜け出します。
Windowsでは、QueueUserAPCを使用して、例外をスローするプロシージャをキューに入れます。このアプローチは私にとってはうまくいきます。
しかし:ブーストミューテックスなどは「変更可能」ではないことがわかりました。 win32では、QueueUserAPCはそれらを中断できません。
非常に遅いですが、Windows(および、VMSやRSXのような前駆体のようなものでは、そのようなことを控えめにするもの)完了時に通知する完了ルーチンでReadFileExなどを使用し、読み取りを早期にキャンセルする必要がある場合はCancelIOを使用します。
Linux / BSDには、柔軟性のない完全に異なる基盤となるAPIがあります。 pthread_killを使用してシグナルを送信すると、読み取り/オープン操作が停止します。
各プラットフォーム、IMHOのこの領域に異なるコードを実装する価値があります。