Event Process.Exited在while循环中重新实例化后超过其对象生命

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Event Process.Exited在while循环中重新实例化后超过其对象生命相关的知识,希望对你有一定的参考价值。

我已经在我的代码中发现了一些我无法解释的行为,即使它工作正常,但没有完全理解正在发生的事情真的困扰着我。下面的脚本创建并运行记事本的n_total进程,但每次只允许运行最多的n_cpus记事本。在开始时,启动n_cpus进程,其余只在一个或多个运行的记事本终止时才开始。用户可以通过关闭其触发事件Process.Exited的窗口来终止每个记事本进程。现在,在循环内部,变量p被重新用于每次需要一个新的记事本时实例化类Process,并且每个对象p都订阅了事件Process.Exited由p.Exited += p_Exited;考虑我们有n_cpus = 3并运行代码直到它生成那3个同时记事本。我希望只有最后一个实例p才会触发事件,因为我重新使用pp.Exited属于该对象,但是没有......无论我关闭什么记事本,事件都会被触发并出现一个新的记事本。这是怎么回事?是否有某种无对象的委托列表EventHandler会记住我创建的每个进程?

using System;
using System.Diagnostics;
using System.Threading;

class Program
{
    // Summary: generates processes for "n_total" notepads allowing for a maximum of "n_cpus"
    //          notepads at each time. Every time a notepad closes another appears until all are run.
    //          A single variable "p" instantiates the class "Process" and the event "p.Exited"
    //          updates the number of running processes "n_running".

    static int n_running = 0; // number of notepads running each time
    static void Main()
    {
        int n_cpus = 3;
        int n_total = 3 * n_cpus;
        int i_run = 0;

        while (i_run < n_total) // Process generating routine until all are run
        {
            if (n_running < n_cpus) // Only a maximum of n_cpus running at each time
            {
                n_running++;
                i_run++;

                Process p = new Process(); // A new object per process
                p.StartInfo.FileName = "cmd.exe";
                p.StartInfo.Arguments = "/c notepad.exe";
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.CreateNoWindow = true;
                p.EnableRaisingEvents = true;
                p.Exited += p_Exited; // Is this associated with a particular object "p", right?
                p.Start();
            }
            else Thread.Sleep(1000); // Waits 1s before checking for new terminated processes
        }
    }

    static private void p_Exited(object sender, EventArgs e)
    {
        n_running--; // Updates the number of active processes. Triggers new future processes
    }
}
答案

我想你可能在这里做了几个错误的假设:

变量不是对象,它们是对象的引用。将对象分配给变量(通过new或其他)时,您不是“替换”或以其他方式删除或删除该变量先前引用的对象。如果没有更多对前一个对象的引用,它可能会在某个时刻被垃圾收集,但该对象仍然存在,并且可能还有其他东西使它“活着”(通过包含对它的引用)。某些编译器优化甚至可能导致相反的情况发生:如果编译器确定该变量未被再次使用,则在引用它的变量超出范围之前,对象可能会收集垃圾。

事件模式可能是C#中“内存泄漏”的最大单一来源,因为许多开发人员都没有意识到,当您注册事件时,正在侦听的对象的生命周期现在已扩展到侦听器的生命周期(因为该对象)正在被监听,现在有一个对监听器的引用,这个引用导致垃圾收集器不收集对象)。由于您的p_Exited是静态的,因此在您取消注册事件之前,您创建的所有Process对象现在都是“rooted”(不会被垃圾回收)。

变量p被重新用于实例化Process类

你实际上甚至没有“重用”p变量。它在循环范围内声明,因此每次循环时,p实际上都是一个“全新的”变量。当涉及到闭包时(即使是C#语言团队got this wrong),这也是一个特别重要的区别。

另一答案

操作系统似乎是为你做的,因为你通过EnableRaisingEvents = true启用了事件。

看到:

以上是关于Event Process.Exited在while循环中重新实例化后超过其对象生命的主要内容,如果未能解决你的问题,请参考以下文章

程序对Process.Exited没有反应

“main process exited, code=exited”错误解决办法

“main process exited, code=exited”错误解决办法

Azkaban Process exited with code 255

CenOS7.1 vncserver@:1.service: control process exited, code=exited status=2

centOS 7一个解决“network.service: control process exited, code=exited status=1”方法