3

C# .NET 3.5

我有一个控制台应用程序正在被计算机上的另一个应用程序调用。此控制台应用程序持续运行,并从“父”进程侦听标准输入上的数据。

但是,当父级停止或终止时,它启动的控制台应用程序会继续运行。在正常情况下,它使用最少的资源等待来自标准输入的输入。但是,一旦父级离开,这个控制台应用程序就会使 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 正在运行 - “Microsoft 应用程序错误报告”程序。它只是......坐在那里,控制台应用程序保留在内存中。

我应该在这里做什么来正确终止进程以避免这种持续的 CPU 峰值和未释放的内存?最终,这需要在没有干预的情况下运行。

PS 我在这里使用命令行应用程序作为“长时间运行的程序”而不是 Windows 服务或 Web 服务,因为父程序只能配置为执行它通过标准输入传递数据的命令行应用程序。(对于那些好奇的人,这是 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)。

4

4 回答 4

5

由于父级退出时控制台输入流被关闭,听起来您的进程正在进入一个硬循环。您是否正在检查返回值Console.In.Read?当流关闭时,它将返回零。那时跳出循环,让您的Main()方法自行退出。

如果您正在运行多个线程,则必须先完成它们,使用Thread.Join或等效。

于 2009-07-29T15:39:09.820 回答
3

要监视进程退出,请使用Process类并订阅Exited事件。

编辑:删除互操作注释。

编辑(回应评论):

大多数情况下,在这种情况下所做的就是将一些数据传回,这样您就可以停止阻塞Console.In.Read方法调用。

因此,假设您IsDone在发现父进程完成时将标志 , 设置为 true。现在,因为您等待的进程不会再发送任何内容,您仍然需要在标准输入上接收某些内容,否则您将永远阻塞。因此,在您的事件处理程序/计时器代码中,向您自己的进程的标准输入写入一些内容(如果您愿意,它甚至可以是一个特殊的值来表示您已完成)。这将使您通过该Console.In.Read方法。一旦退出,检查是否IsDone设置了标志——如果是,停止处理并从你的 main 方法返回——不需要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();
}

假设您在进程列表中只有一个调用进程,只要该进程结束,这个进程也会结束。您还可以为自己的进程放置更好的清理代码,而不是让它突然结束。

于 2009-07-29T15:15:34.173 回答
0

我喜欢 mmr 从孩子那里观察父母过程的想法。如果您有办法在命令行上传递父级的 PID,那就更好了。从父进程内部,您可以获得 PID:

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