WaitForExitAsync 超时

Oma*_*mar 6 c# cmd process

所以我注意到有一个名为WaitForExit接受和 int 作为参数(毫秒)的方法,所以如果进程无法自行退出,我会在几秒钟后杀死它。

像这样的东西。

if (!CMD.WaitForExit(3000))
    CMD.Kill();
Run Code Online (Sandbox Code Playgroud)

问题是,同时我想保存输出,所以我注意到有一个异步方法WaitForExitAsync,但这个方法不接受这些毫秒。

// Wait for exit async...
// Meanwhile save the output till it kills itself.

while (CMD.StandardOutput.ReadLine() != null) 
    standard_output = StandardOutput.ReadLine();
Run Code Online (Sandbox Code Playgroud)

知道如何做到这一点吗?谢谢!

Pet*_*ala 6

你需要使用CancellationTokenSource. 它有一个接受a的ctorTimeSpan

var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(3));
try 
{
   await CMD.WaitForExitAsync(timeoutSignal.Token);
} catch (OperationCanceledException)
{
   CMD.Kill();
}
Run Code Online (Sandbox Code Playgroud)

当 CTS 发出信号时,等待的操作将抛出一个OperationCanceledException. 因此,您需要将调用await包装到try-中catch以正确处理取消的操作。


更新#1:使用退出的异步等待捕获 STDOUT

天真的方法

首先让我与您分享代码的幼稚版本

Console.WriteLine("Launch ping with fifteen retries");
var terminal = Process.Start(new ProcessStartInfo("/sbin/ping")
{
    RedirectStandardOutput = true,
    Arguments = "-c 15 stackoverflow.com",
    UseShellExecute = false,
});

_ = Task.Run(() =>
{
    string line = null;
    while ((line = terminal.StandardOutput.ReadLine()) != null)
        Console.WriteLine(line);
});
            

var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(3));
try
{
    await terminal.WaitForExitAsync(timeoutSignal.Token);
    Console.WriteLine("Ping has been Finished");
}
catch (OperationCanceledException)
{
    terminal.Kill();
    Console.WriteLine("Ping has been Terminated");
}
Run Code Online (Sandbox Code Playgroud)
  • 我在 Macintosh 机器上使用 .NET,所以我没有ping.exe而不是可以运行/sbin/ping命令
  • 我 ping stackoverflow 十五次以确保命令运行超过 3 秒
  • 我已将StandardOutput阅读内容移至单独的线程 ( Task.Run)
    • 否则,取消信号将不会产生任何影响
  • 其余代码与上面相同+调试日志记录

建议的方法

该类Process确实公开了从 异步读取数据的功能,而StandardOutput无需执行额外的技巧

Console.WriteLine("Launch ping with fifteen retries");
var terminal = new Process()
{
    StartInfo = new ProcessStartInfo("/sbin/ping")
    {
        RedirectStandardOutput = true,
        Arguments = "-c 15 stackoverflow.com",
        UseShellExecute = false,
    }
};

terminal.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
terminal.Start();
terminal.BeginOutputReadLine();            

var timeoutSignal = new CancellationTokenSource(TimeSpan.FromSeconds(3));
try
{
    await terminal.WaitForExitAsync(timeoutSignal.Token);
    Console.WriteLine("Ping has been Finished");
}
catch (OperationCanceledException)
{
    terminal.Kill();
    Console.WriteLine("Ping has been Terminated");
}
Run Code Online (Sandbox Code Playgroud)

让我仅强调差异

  • 我们不是立即启动流程,而是首先创建一个流程并指定其StartInfo属性
  • 然后我们订阅该OutputDataReceived事件
    • 它的 EventArgsData属性包含新的可用信息
  • 订阅后我们可以调用该Start方法
  • 最后,我们需要调用该BeginOutputReadLine方法来告诉 Process 在标准输出上有新数据时触发上述事件处理程序