WaitForExitAsync 超时

WaitForExitAsync with a timeout

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

像这样。

if (!CMD.WaitForExit(3000))
    CMD.Kill();

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

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

while (CMD.StandardOutput.ReadLine() != null) 
    standard_output = StandardOutput.ReadLine();

知道怎么做吗?谢谢!

您需要使用 CancellationTokenSource。它有一个 ctor 接受 TimeSpan

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

当 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 whosebug.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");
}
  • 我在 Macintosh 机器上使用 .NET,所以我没有 ping.exe 而不是 运行 /sbin/ping 命令
  • 我 ping Whosebug 十五次以确保命令 运行s 超过 3 秒
  • 我已将 StandardOutput 阅读移至单独的线程 (Task.Run)
    • 否则,取消信号不会有任何效果
  • 其余代码同上+调试日志

建议的方法

Process class 确实提供了从 StandardOutput 异步读取数据的能力,而无需执行额外的技巧

Console.WriteLine("Launch ping with fifteen retries");
var terminal = new Process()
{
    StartInfo = new ProcessStartInfo("/sbin/ping")
    {
        RedirectStandardOutput = true,
        Arguments = "-c 15 whosebug.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");
}

让我只强调不同之处

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

它有一个参数需要毫秒

WaitForExitAsync(process, timeout);

新的 .NET 6 附带 WaitAsync 方法,允许异步等待任务 超时

await process.WaitForExitAsync().WaitAsync(TimeSpan.FromMilliseconds(1000));

if (!process.HasExited)
{
    //timeout!
}