简体   繁体   English

在重定向的 STDOUT 中为子进程缓冲

[英]Buffering in redirected STDOUT for a child process

Code like this can host a console app and listen to its output to STDOUT AND STDERR像这样的代码可以托管一个控制台应用程序并监听它的 output 到 STDOUT 和 STDERR

      Process process = new Process();
      process.StartInfo.FileName = exePath;
      process.StartInfo.UseShellExecute = false;
      process.StartInfo.WorkingDirectory = context.WorkingDirectory;
      process.StartInfo.RedirectStandardOutput = true;
      process.StartInfo.RedirectStandardError = true;
      process.StartInfo.RedirectStandardInput = true; // if you don't and it reads, no more events
      process.StartInfo.UseShellExecute = false;
      process.StartInfo.CreateNoWindow = false;

      process.EnableRaisingEvents = true;
      process.ErrorDataReceived += (sender, dataReceivedEventArgs) =>
      {
        lastbeat = DateTime.UtcNow;
        if (dataReceivedEventArgs.Data != null)
        {
          if (dataReceivedEventArgs.Data.EndsWith("%"))
          {
            context.Logger.Information($"  PROGRESS: {dataReceivedEventArgs.Data}");
          }
          else
          {
            msg.Append(" STDERR (UNHANDLED EXCEPTION): ");
            msg.AppendLine(dataReceivedEventArgs.Data);
            success = false;
          }
        }
      };
      process.OutputDataReceived += (sender, dataReceivedEventArgs) =>
      {
        lastbeat = DateTime.UtcNow;
        if (dataReceivedEventArgs.Data != null)
        {
          if (dataReceivedEventArgs.Data.EndsWith("%"))
          {
            context.Logger.Information($" PROGRESS: {dataReceivedEventArgs.Data}");
          }
          else
          {
            context.Logger.Information($" STDOUT: {dataReceivedEventArgs.Data}");
          }
        }
      };

      lastbeat = DateTime.UtcNow;
      process.Start();
      process.BeginErrorReadLine();
      process.BeginOutputReadLine();
      // wait for the child process, kill it if hearbeats are too slow
      while (!process.HasExited)
      {
        Thread.Sleep(100);
        var elapsed = DateTime.UtcNow - lastbeat;
        if (elapsed.TotalSeconds > heartbeatIntervalSeconds * 3)
        {
          success = false;
          msg.AppendLine("MODULE HEARTBEAT STOPPED, TERMINATING.");
          try
          {
            process.Kill(entireProcessTree: true); // ...and your children's children
          }
          catch (Exception ek)
          {
            msg.AppendLine(ek.Message);
          }
        }
      }

      if (success)
      {
        process.Dispose();
        context.Logger.Debug("MODULE COMPLETED");
        return JobStepResult.Success;
      }
      else
      {
        process.Dispose();
        context.Logger.Debug("MODULE ABORTED");
        throw new Exception(msg.ToString());
      }

The hosted processes are potentially very long running, so we invented a heartbeat mechanism.托管进程可能会运行很长时间,因此我们发明了一种心跳机制。 There's a convention here that STDERR is used for out of band communication so that STDOUT isn't polluted with heartbeat messages.这里有一个约定,STDERR 用于带外通信,这样 STDOUT 就不会被心跳消息污染。 Any line of text written to STDERR that ends with a percent sign is treated as a heartbeat, everything else is a normal error message.写入 STDERR 的任何以百分号结尾的文本行都被视为心跳,其他一切都是正常的错误消息。

We have two hosted modules, one of which works perfectly with heartbeats received in a timely manner, but the other seems to hang until all its output on both STDERR and STDOUT arrives in a flood.我们有两个托管模块,其中一个与及时收到的心跳完美配合,但另一个似乎挂起,直到它在 STDERR 和 STDOUT 上的所有 output 都泛滥成灾。

The hosted modules are written in Lahey FORTRAN.托管模块使用 Lahey FORTRAN 编写。 I have no visibility on that code.我对该代码没有可见性。 I have suggested to the author that she may need to flush her output streams or possibly yield using whatever is the FORTRAN equivalent to Thread.Sleep(10);我向作者建议,她可能需要刷新她的 output 流,或者可能使用与Thread.Sleep(10);

However, it is not beyond the realms of possibility that the problem is on my side.但是,问题出在我这边并不是不可能的。 When the modules are executed manually in a console their output appears at a steady pace with heartbeat messages appearing in a timely manner.当模块在控制台中手动执行时,它们的 output 以稳定的速度出现,心跳消息及时出现。

What governs the behaviour of captured streams?是什么控制了捕获的流的行为?

  • Are they buffered?他们有缓冲吗?
  • Is there any way to influence this?有什么办法可以影响这个吗?

This may be related.这可能是相关的。 Get Live output from Process 从进程中获取实时 output

It seems (see comments) that this is an old problem.似乎(见评论)这是一个老问题。 I pulled my hosting code out into a console app and the problem is manifest there too.我将托管代码提取到控制台应用程序中,问题也很明显。


Codiçil科迪西尔

This doesn't happen when the hosted console app is a dotnet core app.当托管控制台应用程序是 dotnet 核心应用程序时,不会发生这种情况。 Presumably dotnet core apps use ConPTY because that way they can work the same across platforms.大概 dotnet 核心应用程序使用 ConPTY,因为这样它们可以跨平台以相同的方式工作。

This is an old problem.这是一个老问题。 An application can detect if they are running in a console and if not, choose to buffer their output.应用程序可以检测它们是否在控制台中运行,如果没有,则选择缓冲它们的 output。 For example, this is something that the microsoft C runtime does deliberately whenever you call printf .例如,这是 microsoft C 运行时在您调用printf时故意执行的操作。

An application should not buffer writes to stderr, as errors should be made available before your program has a chance to crash and wipe any buffer.应用程序不应缓冲对 stderr 的写入,因为错误应该在您的程序有机会崩溃并擦除任何缓冲区之前提供。 However, there's nothing to force that rule.但是,没有什么可以强制执行该规则。

There are old solutions to this problem.这个问题有旧的解决方案。 By creating an off-screen console you can detect when output is written to the console buffer.通过创建屏幕外控制台,您可以检测 output 何时写入控制台缓冲区。 There's a write up on Code Project that talks about this issue and solution in more detail.有一篇关于Code Project的文章更详细地讨论了这个问题和解决方案。

More recently, Microsoft has modernised their console infrastructure.最近,微软对其控制台基础设施进行了现代化改造。 If you write to a modern console, your output is converted to a UTF-8 stream with embedded VT escape sequences.如果您写入现代控制台,您的 output 将转换为带有嵌入式 VT 转义序列的 UTF-8 stream。 A standard that the rest of the world has been using for decades.世界上rest已经使用了几十年的标准。 For more details you can read their blog series on that work here .有关更多详细信息,您可以在此处阅读他们关于该工作的博客系列。

I believe that it should be possible to build a new modern workaround.我相信应该有可能建立一个新的现代解决方法。 A stub process, similar to the Code Project link above, that uses these new pseudo console API's to launch a child process, capture console I/O and pipe that output unbuffered to its own stdio handles.一个存根进程,类似于上面的代码项目链接,它使用这些新的伪控制台API 来启动子进程,捕获控制台 I/O 和 pipe,output 未缓冲到它自己的 stdio 句柄。 I would be nice if such a stub process were distributed in windows, or by some other 3rd party, but I haven't found one yet.如果这样的存根进程分布在 windows 或其他一些第三方中,我会很好,但我还没有找到。

There are a couple of samples available in github.com/microsoft/terminal that would be a good starting point if you wanted to create your own stub. github.com/microsoft/terminal中有几个示例,如果您想创建自己的存根,这将是一个很好的起点。

However I believe that using either this solution, or the old workaround would still merge the output and error streams together.但是,我相信使用此解决方案或旧的解决方法仍会将 output 和错误流合并在一起。

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

相关问题 控制台中的处理标准输出与重定向时不同 - Process stdout different in console vs when redirected 关于System.Diagnostics.Process中的重定向标准输出 - About redirected stdout in System.Diagnostics.Process 从子进程的stdout同步异步读取 - Synchronization of asynchronous reading from stdout of child process C#异步读取子进程的stdout - C# Read stdout of child process asynchronously 将子进程的输出(stdout,stderr)重定向到Visual Studio中的“输出”窗口 - Redirect the output (stdout, stderr) of a child process to the Output window in Visual Studio Linux 上的 .NET Core:标准输出是否重定向? - .NET Core on Linux: Is stdout redirected? 从子控制台进程重定向到父控制台进程的控制台输出失败 - Redirected Console Output from a Child Console Process to a Parent Console Process Fails 将父进程的控制台(stdout,标准错误等)输出重定向到子进程(反向) - Redirecting Console (stdout, standard error, etc.) output of a parent process to child process (reverse) 异步写入重定向的标准输出是同步还是异步? - Are async writes to a redirected stdout synchronous or async? 同步读取进程的标准输出 - Synchronously reading stdout of a process
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM