即使进程正在运行,Process.HasExited也会返回true?

时间:2010-03-25 21:52:21

标签: c# process exit terminate

我一直在观察Process.HasExited有时会返回true,即使进程仍在运行。

下面的代码启动一个名为“testprogram.exe”的进程,然后等待它退出。问题是有时我会抛出异常;似乎即使HasExited返回true,过程本身在系统中仍然存在 - 这怎么可能???

我的程序在它终止之前就写入了一个日志文件,因此我需要在读取之前确保该日志文件存在(也就是进程已终止/完成)。不断检查它的存在不是一种选择。

// Create new process object
process = new Process();

// Setup event handlers
process.EnableRaisingEvents = true;
process.OutputDataReceived += OutputDataReceivedEvent;
process.ErrorDataReceived += ErrorDataReceivedEvent;
process.Exited += ProgramExitedEvent;

// Setup start info
ProcessStartInfo psi = new ProcessStartInfo
                           {
                               FileName = ExePath,
                               // Must be false to redirect IO
                               UseShellExecute = false,
                               RedirectStandardOutput = true,
                               RedirectStandardError = true,
                               Arguments = arguments
                           };

process.StartInfo = psi;

// Start the program
process.Start();

while (!process.HasExited)
    Thread.Sleep( 500 );

Process[] p = Process.GetProcessesByName( "testprogram" );

if ( p.Length != 0 )
    throw new Exception("Oh oh");

更新:我刚尝试等待process.WaitForExit()而不是轮询循环,结果完全相同。

补充:上述代码只是为了证明一个“更清晰”的问题。说清楚;我的问题并不是Process.GetProcessesByName( "testprogram" );HasExited设置为true后,我仍然可以暂停该过程。

真正的问题是我在外部运行的程序写入一个文件 - 它在它终止之前(优雅地)。我使用HasExited来检查进程何时完成,因此我知道我可以读取文件(因为进程退出了!),但是HasExited似乎有时返回true程序尚未将文件写入磁盘。以下示例代码说明了确切的问题:

// Start the program
process.Start();

while (!process.HasExited)
    Thread.Sleep( 500 );
// Could also be process.WaitForExit(), makes no difference to the result

// Now the process has quit, I can read the file it has exported
if ( !File.Exists( xmlFile ) )
{
    // But this exception is thrown occasionally, why?
    throw new Exception("xml file not found");
}

11 个答案:

答案 0 :(得分:11)

我意识到这是一篇很老的帖子,但是为了找出为什么我的应用程序在应用程序打开之前运行Exited事件,我发现了一些东西,但我对将来遇到这个问题的人有用。

启动进程时,会为其分配PID。 如果随后通过“用户帐户控制”对话框提示用户并选择“是”,则重新启动该过程并为其分配新的PID。

我和他坐了几个小时,希望这可以节省一些时间。

答案 1 :(得分:7)

我建议您尝试这种方式:

process.Start();

while (!process.HasExited)
{
    // Discard cached information about the process.
    process.Refresh();

    // Just a little check!
    Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString());

    Thread.Sleep(500);
}

foreach (Process current in Process.GetProcessesByName("testprogram"))
{
    if ((current.Id == process.Id) && !current.HasExited)
        throw new Exception("Oh oh!");
}

无论如何......在HasExited的MSDN页面中我正在阅读以下高亮显示的注释:

  

标准输出重定向到异步事件时   处理程序,输出处理可能没有   当此属性返回true时完成。确保异步   事件处理已经完成,调用WaitForExit()重载   在检查HasExited之前没有参数。

当您重定向所有内容时,这可能会以某种方式与您的问题相关联。

答案 2 :(得分:4)

因此,为了进一步调查问题的根本原因,您应该使用Process Monitor查看实际发生的情况。只需启动它并包含外部程序和您自己的工具,并让它记录发生的情况。

在日志中,您应该看到外部工具如何写入输出文件以及如何打开该文件。但是在这个日志中你应该看到所有这些访问的顺序。

我想到的第一件事就是Process类没有说谎而且当它说明时,过程真的消失了。所以问题是在这个时间点,文件似乎仍然没有完全可用。我认为这是OS 的问题,因为它将文件的某些部分保留在未完全写入磁盘的缓存中,并且该工具只是退出自己而不刷新其文件句柄。

考虑到这一点,您应该在日志中看到外部工具创建了文件,退出并在文件将被刷新/关闭之后(通过操作系统[当您在日志中找到此点时可能删除任何过滤器] )。

因此,如果我的假设是正确的,那么根本原因就是外部工具的不良行为,你无法改变,因此导致在进程退出后只需等待一点,并希望超时足够长操作系统刷新/关闭文件(可能尝试在超时循环中打开文件,直到成功为止)。

答案 3 :(得分:4)

首先,你确定testprogram不会产生自己的进程并退出而不等待该进程完成吗?我们在这里处理某种竞争条件,测试程序可能很重要。

我想说的第二点是 - “我需要绝对确定这个日志文件存在”。好吧,没有这样的事情。您可以进行检查,然后文件就消失了。解决这个问题的常用方法不是检查,而是要执行您想要对文件执行的操作。继续,阅读它,捕获异常,如果事情看起来不稳定并且你不想改变任何东西,则重试。如果系统中有多个actor(线程或其他),则功能性检查和操作不会很好。

接下来是一堆随意的想法。

您是否尝试过使用FileSystemWatcher而不依赖于流程完成?

如果您尝试在该过程中尝试读取该文件(不检查它是否存在,而是代理),它是否会变得更好。退出事件? [它不应该]

系统健康吗?事件日志中有什么可疑的东西吗?

是否可以参与一些非常积极的防病毒政策?

(如果没有看到所有代码并查看测试程序,就无法说清楚。)

答案 4 :(得分:4)

我知道,这是一个老帖子,但也许我可以帮助别人。
Process类可能会出乎意料!如果流程已退出,如果流程以管理员权限运行且您的程序仅具有用户权限,则HasExited将返回true。

答案 5 :(得分:2)

有两种可能性,进程对象继续保持对进程的引用,因此它已退出,但尚未删除。或者您正在运行该进程的第二个实例。您还应该比较进程ID以确保。试试这个。

    ....

    // Start the program
    process.Start();


    while (!process.HasExited)
        Thread.Sleep( 500 );

    Process[] p = Process.GetProcessesByName( "testprogram" );
    if ( p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited)
        throw new Exception("Oh oh");

答案 6 :(得分:1)

首先,使用Process.WaitForExit而不是轮询它是否存在问题?

无论如何,从技术上讲,进程从可用的角度退出是可能的,但是当进程像刷新磁盘缓存这样时,进程仍然是短暂的。日志文件特别大(或者它在磁盘写入时执行的任何操作都很重)?

答案 7 :(得分:1)

根据HasExited的{​​{3}}。

  

如果句柄对该过程开放,   操作系统发布了   进程内存时进程内存   退出,但保留行政   有关过程的信息,例如   句柄,退出代码和退出时间。

可能没有关系,但值得注意。

如果这只是问题的1/10,并且该过程在一秒之后消失,取决于您对HasExited的使用情况,请尝试在HasExited检查工作后添加另一个延迟,例如

while (!process.HasExited)
    DoStuff();
Thread.Sleep(500);
Cleanup();

并查看问题是否仍然存在。

就个人而言,我总是只使用Exited事件处理程序而不是任何类型的轮询,以及围绕System.Diagnostics.Process的简单自定义包装来处理线程安全,包装对{{}的调用。 1}}后跟CloseMainWindow(),最后是WaitForExit(timeout),记录等等,从未遇到过问题。

答案 8 :(得分:1)

问题可能出在测试程序中?这段代码是否很好地刷新/关闭等?在我看来,如果testprogram将文件写入磁盘,该文件应至少可用(空或不)

答案 9 :(得分:1)

在检查进程是否已退出之前使用process_name.Refresh()Refresh()将清除与该过程相关的所有缓存信息。

答案 10 :(得分:0)

如果您有Web应用程序,并且您的外部程序/进程正在生成文件(写入磁盘),请检查您的IIS是否有权写入该文件夹(如果没有为IIS用户添加属性安全性添加权限),这就是原因在我的情况下,我正在接收process.HasExited = true,但是从进程中生成的文件没有完成,经过一段时间的努力,我向进程正在扭曲的文件夹和process.Refresh()添加完全权限,如上所述Zarathos一切都按预期工作。