繁体   English   中英

即使Process.HasExited为true,Process.WaitForExit也不会返回

[英]Process.WaitForExit doesn't return even though Process.HasExited is true

我使用Process.Start启动批处理文件。 批处理文件使用“ START”命令并行启动多个程序,然后退出。

批处理文件完成后,Process.HasExited变为true,并且Process.ExitCode包含正确的退出代码。

但是,当我调用Process.WaitForExit()时,它将挂起/永不返回。

以下代码演示了该问题。 它创建一个批处理文件,启动它,然后打印:

Process is still running...
Batch file is done!
Process has exited. Exit code: 123
Calling WaitForExit()...

然后应打印:

WaitForExit returned.

...但是它从来没有(即使HasExited为true并且我们已经有一个ExitCode)。

open System.IO
open System.Diagnostics
open System.Threading

let foobat = """
  START ping -t localhost
  START ping -t google.com
  ECHO Batch file is done!
  EXIT /B 123
"""

File.WriteAllText("foo.bat", foobat)

use p = new Process(StartInfo = ProcessStartInfo("foo.bat",
                                                 UseShellExecute = false,
                                                 RedirectStandardOutput = true,
                                                 RedirectStandardError = true))

let onOutput = DataReceivedEventHandler(fun _ args -> printfn "%s" args.Data)

p.OutputDataReceived.AddHandler onOutput
p.ErrorDataReceived.AddHandler onOutput

p.Start() |> ignore

p.BeginErrorReadLine()
p.BeginOutputReadLine()

while not p.HasExited do
  printfn "Process is still running..."
  Thread.Sleep(1000)

printfn "Process has exited. Exit code: %d" p.ExitCode

printfn "Calling WaitForExit()..."
p.WaitForExit()|> ignore
printfn "WaitForExit returned."

我注意到只有当批处理文件包含“ START”命令并且重定向标准输出和/或标准错误时,才会发生这种情况。

为什么WaitForExit()永不返回?

等待此类过程退出的正确方法是什么?

仅轮询Process.HasExited是否安全,否则可能导致其他问题?

PS .:我刚刚注意到,当进程退出时,调用WaitForExit( 100000 )具有巨大的超时时间(肯定不会过期)会立即返回。 怪异的 没有超时,它将挂起。

在StandardOutput和StandardError的基于事件的异步处理的特定实现中,这似乎是一种伪像(我会说“ bug”)。

我注意到,尽管我能够轻松地重现您的问题,但是只需运行您提供的代码(顺便说一下,就是优秀的代码示例!:)),该过程实际上并没有无限期地挂起。 相反,一旦两个已启动的子进程本身退出,它就会从WaitForExit()返回。

这似乎是Process类实现的有意部分。 特别是,在Process.WaitForExit()方法中,一旦它完成了对进程句柄的等待,它将检查是否已创建用于stdout或stderr的读取器; 如果是这样,并且如果WaitForExit()调用的超时值为“ infinite”(即-1 ),则代码实际上将等待读取器上的流结束。

仅当BeginOutputReadLine()BeginErrorReadLine()方法时, BeginOutputReadLine()创建各个阅读器。 直到子进程关闭,stdout和stderr流本身才关闭。 因此,等待这些流的末尾将阻塞,直到发生这种情况。

WaitForExit()行为应有所不同,具体取决于是否调用了启动基于事件的流读取的方法之一,特别是鉴于直接读取这些流不会导致WaitForExit()以这种方式运行, API中的不一致,使理解和使用变得更加困难。 虽然我个人将其称为错误,但我认为Process类的实现者有可能意识到这种不一致并故意创建了这种不一致。

无论如何,解决方法是直接读取StandardOutput和StandardError,而不使用API​​的基于事件的部分。 (当然,如果一个人的代码要在这些流上等待,那么在子进程关闭之前,人们会看到相同的阻塞行为。)

例如(C#,因为我对F#的了解不够,无法快速将这样的代码示例拍打在一起:)):

using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace TestSO26713374WaitForExit
{
    class Program
    {
        static void Main(string[] args)
        {
            string foobat =
@"START ping -t localhost
START ping -t google.com
ECHO Batch file is done!
EXIT /B 123
";

            File.WriteAllText("foo.bat", foobat);

            Process p = new Process { StartInfo =
                new ProcessStartInfo("foo.bat")
                {
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true
                } };

            p.Start();

            var _ = ConsumeReader(p.StandardOutput);
            _ = ConsumeReader(p.StandardError);

            Console.WriteLine("Calling WaitForExit()...");
            p.WaitForExit();
            Console.WriteLine("Process has exited. Exit code: {0}", p.ExitCode);
            Console.WriteLine("WaitForExit returned.");
        }

        async static Task ConsumeReader(TextReader reader)
        {
            string text;

            while ((text = await reader.ReadLineAsync()) != null)
            {
                Console.WriteLine(text);
            }
        }
    }
}

希望上述解决方法或类似方法可以解决您遇到的基本问题。 感谢评论员Niels Vorgaard Christensen将我引导到WaitForExit()方法中有问题的行,以便我可以改善此答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM