プログラムが独自のプロセスを終了する正しい方法は何ですか(Windows)
質問
C#.NET 3.5
コンピューター上の別のアプリケーションによって呼び出されているコンソールアプリケーションがあります。このコンソールアプリは継続的に実行され、「親」からのstdinのデータをリッスンします。プロセス。
ただし、親が停止または強制終了されると、開始されたコンソールアプリは続行します。通常の状況では、最小限のリソースを使用して、stdinからの入力を待機して待機します。ただし、親がなくなるとすぐに、このコンソールアプリはCPUを急上昇させ、実行中のコアをほぼ100%の使用率で枯渇させます。これは、プロセスを手動で強制終了するまで続きます。
理想的には、呼び出し元の親は、特にこれが通常の(例外的ではない)" stop"の下で行われているため、それ以降をクリーンアップします。条件。残念ながら、この親プロセスは手に負えません。
最初に考えたのは、コンソールアプリ内から呼び出し元の親を取得し、そのPIDを監視することでした。親プロセスがなくなると、コンソールアプリ自体が終了します。現在、私はこれを次のようにしています:
Process process = Process.GetCurrentProcess();
m_ParentPID = 0;
using (ManagementObject mgmtObj = new ManagementObject("win32_process.handle='" + process.Id.ToString() + "'"))
{
mgmtObj.Get();
m_ParentPID = Convert.ToInt32(mgmtObj["ParentProcessId"]);
}
string parentProcessName = Process.GetProcessById(m_ParentPID).ProcessName;
Log("Parent Process: " + parentProcessName + Environment.NewLine);
// Create a timer for monitoring self.
Timer timer = new Timer(new TimerCallback(sender =>
{
if (m_ParentPID != 0)
{
Process parent = System.Diagnostics.Process.GetProcessById(m_ParentPID);
if (parent == null)
{
Log("Parent process stopped/killed. Terminating self.");
System.Environment.Exit(0);
}
}
}));
// Kick on the timer
timer.Change(m_ExitWatcherFrequency, m_ExitWatcherFrequency);
これは部分的にしか機能しません-CPUスパイクを停止しますが、Sysinternalsの素晴らしいプロセスモニターからプロセスを見ると、DW20.exeが実行されていることがわかります-「Microsoft Application Error Reporting」プログラム。そして、それは...そこにあり、コンソールアプリはメモリに残ります。
この継続的なCPUスパイクと未リリースのメモリを回避するために、プロセスを正しく終了するにはここで何をすべきですか?最終的に、これは介入なしで実行する必要があります。
PSここでは、「長時間実行プログラム」としてコマンドラインアプリケーションを使用しています。親プログラムは、stdinを介してデータを渡すコマンドラインアプリのみを実行するように構成できるため、WindowsサービスまたはWebサービスではありません。 (好奇心are盛な人のために、これは外部認証を使用したejabberdです。)
編集:
標準入力からの入力を待機するコードは次のとおりです。
// Read data from stdin
char[] charray = new char[maxbuflen];
read = Console.In.Read(charray, 0, 2);
前に言及しましたが、親が終了すると、コンソールアプリはCPUで狂ってしまいます。 Visual Studioからデバッガをアタッチしましたが、実際にはまだその行にConsole.In.Readがあります。理論的には、自己監視タイマーがトリガーして親がなくなったことを確認すると、他のスレッドがそのRead()行にあるときにSystem.Environment.Exit(0)を試行します。
解決
親が終了するときにコンソール入力ストリームが閉じられているため、プロセスがハードループに入っているようです。 Console.In.Read
からの戻り値を確認していますか?ストリームが閉じられると、ゼロを返します。その時点でループから抜け出し、 Main()
メソッドを単独で終了させます。
また、複数のスレッドを実行している場合は、 Thread.Join
または同等のものを使用して、最初に終了する必要があります。
他のヒント
プロセスの終了を監視するには、プロセスクラス、および Exited イベント。
編集:相互運用コメントを削除しました。
編集(コメントへの応答):
ほとんどの場合、このような状況で行われるのは、一部のデータを戻すことです。そのため、 Console.In.Read
メソッド呼び出しでブロックを停止できます。
したがって、親プロセスが完了したことがわかったときに、フラグ IsDone
をtrueに設定するとします。今、あなたが待っているプロセスはもう何も送信しないので、標準入力で何かを受信する必要があります。そうしないと、永遠にブロックされます。そのため、イベントハンドラー/タイマーコードで、独自のプロセスの標準入力に何かを記述します(必要に応じて、完了を通知する特別な値になることもあります)。これにより、 Console.In.Read
メソッドを通過できます。いったん出たら、 IsDone
フラグが設定されているかどうかを確認します。設定されている場合、処理を停止してメインメソッドから戻ります。 System.Environment.Exit
は不要です。
プロセステーブル内の呼び出し元アプリの存在を基本的に監視するスレッドをコンソールアプリに追加して、これを解決します。これはすべて私の頭の外ですが、ここにいくつかの擬似コードがあります:
using System.Thread;
using System.Diagnostics;
main(){
Thread monitoringThread = new Thread(new ThreadStart(monitor));
monitoringThread.Name = "Monitor";
monitoringThread.Start();
}
および機能モニター:
void monitor(){
Process[] theCallers = Process.GetProcessesByName("CallingProcessName");
theCallers[0].WaitForExit();
Process.GetCurrentProcess().Kill();
}
プロセスリストに呼び出しプロセスが1つしかない場合、そのプロセスが終了するとすぐに、このプロセスも同様になります。突然終了するのではなく、独自のプロセスにもっときれいなクリーンアップコードを追加することもできます。
私は、mmrが子から親プロセスを監視するというアイデアが好きです。コマンドラインで親のPIDを渡す方法があれば、それはさらに良いでしょう。親プロセス内から、PIDを取得できます:
System.Diagnostics.Process.GetCurrentProcess().Id