3

C#.NET 3.5

コンピューター上の別のアプリケーションから呼び出されているコンソールアプリケーションがあります。このコンソールアプリは継続的に実行され、「親」プロセスからstdinのデータをリッスンします。

ただし、親が停止または強制終了された場合、親が起動したコンソールアプリは続行されます。通常の状況では、最小限のリソースを使用して、stdinからの入力を待機してアイドル状態になります。ただし、親がなくなるとすぐに、このコンソールアプリはCPUを急上昇させ、実行中のコアをほぼ100%の使用率で枯渇させます。これは、プロセスを手動で強制終了するまで続きます。

理想的には、呼び出し元の親は、特にこれが通常の(例外ではない)「停止」状態で発生しているため、それ自体の後でクリーンアップします。残念ながら、この親プロセスは私の手に負えません。

私の最初の考えは、コンソールアプリ内から呼び出し元の親を取得し、その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が実行されていることがわかります-「MicrosoftApplicationErrorReporting」プログラム。そして、それはただ...そこにあり、コンソールアプリはメモリに残ります。

この継続的なCPUスパイクと解放されていないメモリを回避するために、プロセスを正しく終了するには、ここで何をする必要がありますか?最終的に、これは介入なしで実行する必要があります。

PSここでは、WindowsサービスやWebサービスではなく、「長時間実行プログラム」としてコマンドラインアプリケーションを使用しています。親プログラムは、stdinを介してデータを渡すコマンドラインアプリを実行するようにのみ構成できるためです。(好奇心旺盛な人のために、これは外部認証を使用したejabberdです)。

編集:

stdinからの入力を待機するコードは次のとおりです。

// 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)を試行します。

4

4 に答える 4

5

親が終了するとコンソール入力ストリームが閉じられるため、プロセスがハードループに陥っているようです。からの戻り値を確認していますConsole.In.Readか? ストリームが閉じられると、0 が返されます。その時点で、ループから抜け出し、メソッドを自動的に終了させますMain()

また、複数のスレッドを実行している場合は、Thread.Joinまたは同等のものを使用して、最初に終了する必要があります。

于 2009-07-29T15:39:09.820 に答える
3

プロセスの終了を監視するには、Processクラスを使用し、Exitedイベントをサブスクライブします。

編集:相互運用コメントを削除しました。

編集(コメントに応じて):

ほとんどの場合、このような状況で行われるのは、Console.In.Readメソッド呼び出しでのブロックを停止できるように、データを戻すことです。

IsDoneしたがって、親プロセスが完了したことを発見したときに、フラグ を true に設定するとします。待っているプロセスはもう何も送信しないため、標準入力で何かを受信する必要があります。そうしないと、永久にブロックされます。そのため、イベント ハンドラー/タイマー コードで、独自のプロセスの標準入力に何かを書き込みます (必要に応じて、完了を知らせる特別な値にすることもできます)。これにより、メソッドを通過できますConsole.In.Read。終了したら、IsDoneフラグが設定されているかどうかを確認します。フラグが設定されている場合は、処理を停止してメイン メソッドから戻ります。必要ありませんSystem.Environment.Exit

于 2009-07-29T14:24:31.467 に答える
2

基本的にプロセステーブル内の呼び出し元アプリの存在を監視するスレッドをコンソールアプリに追加することで、これを解決します。これはすべて私の頭から離れていますが、ここにいくつかの擬似コードがあります:

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つしかない場合、そのプロセスが終了するとすぐに、このプロセスも終了します。また、突然終了するのではなく、独自のプロセス用にさらに優れたクリーンアップコードを配置することもできます。

于 2009-07-29T15:15:34.173 に答える
0

私は、子から親のプロセスを監視するというmmrのアイデアが好きです。コマンドラインで親のPIDを渡す方法があれば、それはさらに良いでしょう。親プロセス内から、PIDを取得できます。

System.Diagnostics.Process.GetCurrentProcess().Id
于 2009-07-29T15:58:03.650 に答える