web-dev-qa-db-ja.com

C ++:任意の関数呼び出しのタイムアウトを実装するにはどうすればよいですか?

残念ながら、特定の時間内に終了しないことがあるライブラリ関数を呼び出す必要があります。関数を呼び出す方法はありますが、n秒以内に終了しない場合は中止しますか?

関数を変更できないため、中止条件を直接関数に入れることができません。 関数に外部的にタイムアウトを追加するする必要があります。

それを(ブースト)スレッドとして開始し、特定の時間後に終了できるようにすることは、おそらく可能な解決策ですか?そのようなものは機能しますか?私は実際に関数がnotスレッドセーフであると信じていますが、として実行しても問題ありませんonlyシングルスレッドでしょ?他の(より良い)解決策はありますか?

27
Frank

boost::threadを生成して、APIを呼び出すことができます。

boost::thread api_caller(::api_function, arg1, arg2);
if (api_caller.timed_join(boost::posix_time::milliseconds(500)))
{
    // API call returned within 500ms
}
else
{
    // API call timed out
}

ただし、Boostではワーカースレッドを強制終了することはできません。この例では、孤立しているだけです。

取得したリソースを解放しない可能性があるため、API呼び出しの動作に注意する必要があります。

18
Ben Straub

これを実現する唯一の安全な方法は、アプリケーションのプロキシとしてライブラリ関数を呼び出す別のsandboxプロセスを生成することだと思います。アプリケーションとプロキシの間に何らかのタイプのIPCを実装する必要があります。IPC応答の読み取り時にタイムアウトを実装することは、かなり簡単です。タイムアウトが原因で読み取りが失敗した場合、アプリケーションの正常性を危険にさらすことなく、プロキシを安全に終了できます。

15
An̲̳̳drew

あなたが話していることは、通常、「ウォッチドッグ」システムと呼ばれます。ウォッチドッグは通常、他のすべてのスレッドのステータスをチェックする2番目のスレッドです。ウォッチドッグは通常、定期的に実行するように設定されています。他のスレッドから応答がない場合、ウォッチドッグはユーザーに通知するか、可能であれば問題のあるスレッドを強制終了することもできます(アプリケーションによって異なります)。

8
Doug T.
3
Bruno Martinez

スレッドの問題は、スレッドの終了後に解放できないリソースがあることです。リリースする必要のあるリソースを取得しない場合は、スレッドを使用してください。

3
Mykola Golubyev

問題は、インプロセスソリューションを使用すると関数からのサポートなしで潜在的に無効な状態になってしまうことです。

例:メモリ割り当ての実行中にスレッドを終了すると、プロセスヒープが破損する可能性があります。

したがって、通話を終了することもできますが、その場合はプロセスも終了する必要があります。多くの場合、破壊的な副作用の可能性は小さいですが、私はそれに計算を賭けません。

ベン・ストラウブが示唆しているように、スレッドを孤立させることができます。それを最低の優先順位に置き、無限に実行させます。もちろん、これは限られた解決策にすぎません。スレッドがリソースを消費すると(おそらく)、システムの速度が低下し、プロセスごとのスレッドにも制限があります(通常はスレッドスタックのアドレス空間が原因です)。

一般的に、私は外部プロセスソリューションを好みます。単純なパターンは次のとおりです。
入力データをファイルに書き込み、ファイルを引数として外部プロセスを開始します。外部プロセスは、進行状況(存在する場合)を監視可能なディスクファイルに書き込み、プロセスを開始した場所から再開できる場合もあります。結果はディスクに書き込まれ、親プロセスはでそれらを読み取ることができます。

プロセスを終了するときでも、外部リソース(ファイルなど)へのアクセスの同期、および破棄されたミューティス、半分書き込まれたファイルなどの処理方法を処理する必要があります。ただし、これは通常、堅牢なソリューションへの道です。

3
peterchen

Orphanプロセスを使用して起動し、実行時間を計ります。時間がなくなった場合は、OSを呼び出して強制終了します。

レース条件を回避する方法。このパターンについて:

  • argsに保存するファイルを作成します(もちろん、すべてがVALとして渡されます)。孤立したプロセスは、このファイルからのデータの読み取りのみが許可されています。

  • Orphanは入力データを処理し、結果値を含む出力ファイルを作成して閉じます。

  • すべてが完了した場合にのみ、Orphanは入力ファイルを削除します。これは、作業が完了したことをマスタープロセスに通知する事実です。

これにより、マスターが最初に入力ファイルの不在に気づき、出力ファイルの読み取りを開始するため、半分書き込まれたファイルの読み取りの問題が回避されます。これは確実に完了します(入力を削除する前に閉じられ、OS呼び出しスタックがシーケンシャルであるため)。

1
jpinto3912

「残念ながら、特定の時間内に終了しないことがあるライブラリ関数を呼び出す必要があります。関数を呼び出す方法はありますが、n秒以内に終了しない場合は中止しますか?」

簡単な答えはノーです。それは通常問題です...呼び出し自体はいつか終了する必要があります(独自のタイムアウトを実装する)が、呼び出しのブロックは通常問題です(gethostbyname()など)。それはあなたではなく彼ら(またはシステム)のタイムアウト次第だからです。

したがって、可能な限り、スレッドで実行されているコードを必要に応じてクリーンに終了するようにしてください。コード自体がエラーを検出して処理する必要があります。メッセージを送信したり、ステータスを設定したりして、メイン(または別の)スレッドが何が起こったかを知ることができます。

個人的な好みですが、高可用性システムでは、スレッドが頻繁に回転し(ただし、ビジーロックは発生しません)、特定のタイムアウト、非ブロッキング関数の呼び出し、正確な終了条件が設定されているのが好きです。グローバル変数またはスレッド固有の「done」変数は、クリーンな終了のためのトリックを実行します。

1
robguima

必要なのは、関数呼び出しの結果を保持できるスレッドと Future Object です。

ブーストの使用例については、 ここ を参照してください。

タイムアウト後に将来を確認する必要があり、設定されていない場合はそれに応じて行動します。

1
lothar