【问题标题】:What is the correct way for a program to terminate its own process (Windows)程序终止自己的进程的正确方法是什么(Windows)
【发布时间】:2009-07-29 23:58:03
【问题描述】:

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 峰值和未释放的内存?最终,这需要在没有干预的情况下运行。

附:我在这里使用命令行应用程序作为“长时间运行的程序”而不是 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)。

【问题讨论】:

    标签: .net windows process kill terminate


    【解决方案1】:

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

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

    【讨论】:

    • 嗯,好点devstuff。我并没有真正停下来思考为什么 CPU 会出现峰值,但是因为 Console.In.Read 总是返回零而连续循环很有意义。
    • 这里唯一需要注意的是,如果您在正常操作期间返回 0 字节,您将错误地终止控制台应用程序。
    • @nader:只有在父进程关闭它们的输出流时才会发生这种情况,所以这是一个退出指示符是一个不错的选择。 :-)
    • @devstuff:有道理。我显然不熟悉 Read 的工作方式:)
    【解决方案2】:

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

    编辑:删除互操作注释。

    编辑(响应 cmets):

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

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

    【讨论】:

    • 但是当程序看到其他进程已退出时,我该怎么做才能让程序自行终止?另外,我无权访问父程序,因此无法修改它以使其向子程序发送任何信号。
    • 您的控制台应用程序究竟是如何等待输入的?某种循环?您可以发布代码(或伪代码)吗?这对于找出最好的方法是有用的。 System.Environment.Exit(0) 应该可以工作——所以我很好奇是什么导致了问题。
    • 根据您的问题更新了一些附加信息,纳德
    • +1 表示 IsDone 的想法。在我看到 devstuff 的答案之前,我先尝试了这个,它对我很有用。但最终还是选择了 devstuff 的答案,因为它更干净并且直接查看 stdin 状态,而不是像我试图做的那样查看父母的迂回方式。谢谢!
    【解决方案3】:

    我会通过向控制台应用程序添加一个线程来解决这个问题,该线程基本上监控进程表中调用应用程序的存在。这完全不是我的想法,但这里有一些伪代码:

    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();
    }
    

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

    【讨论】:

    • 这不是我对上面的 Timer() 所做的吗? (就监控而言)。 Process.GetCurrentProcess().Kill() 与 System.Environment.Exit(0) 有何不同?
    • 这个解决方案没有观察频率,只是在进程终止时获取一个事件,然后继续线程的其余部分。我在自己的代码中使用了 Kill() 而不是 Exit(0) ,因为当 exit 通过终结器等时,kill 现在可以工作。如果你不关心终结器,那么 kill 会很好,但如果你想清理,那么 exit(0) 可能会更好。但是,我不确定,可能是您的终结器导致您的处理高峰。
    • 啊,我明白你的意思了。所以你这里只有另一个线程在等待退出,而不是轮询一个不存在的进程。
    • 完全正确。据我所知,轮询也可能导致飙升。
    【解决方案4】:

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

    System.Diagnostics.Process.GetCurrentProcess().Id
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-09
      • 1970-01-01
      • 2018-05-06
      • 1970-01-01
      • 1970-01-01
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多