C# System.Diagnostics.Process 重定向大量数据的标准输出

发布于 2024-09-03 10:34:04 字数 228 浏览 5 评论 0原文

我从 .NET 应用程序运行一个 exe,并尝试将标准输出重定向到流读取器。问题是当我执行

myprocess.exe >> 时out.txt

out.txt 接近 14mb。 当我执行命令行版本时,速度非常快,但是当我从 csharp 应用程序运行该进程时,速度非常慢,因为我相信默认的流读取器每 4096 字节刷新一次。

有没有办法更改 Process 对象的默认流读取器?

I running an exe from a .NET app and trying to redirect standard out to a streamreader. The problem is that when I do

myprocess.exe >> out.txt

out.txt is close to 14mb.
When I do the command line version it is very fast but when I run the process from my csharp app it is excruciatingly slow because I believe the default streamreader flushes every 4096 bytes.

Is there a way to change the default stream reader for the Process object?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

淡墨 2024-09-10 10:34:04

我没有尝试过,但看起来异步方法可能会提供更好的性能。不要使用 process.StandardOutput,而是尝试以下方法:

Process process = Process
    .Start(new ProcessStartInfo("a.exe"){RedirectStandardOutput = true});
if (process != null)
{
    process.OutputDataReceived += ((sender, e) =>
                                   {
                                       string consoleLine = e.Data;
                                       //handle data
                                   });
    process.BeginOutputReadLine();
}

I haven't tried, but it looks like the asynchronous methods may offer better performance. Instead of using process.StandardOutput, try this method instead:

Process process = Process
    .Start(new ProcessStartInfo("a.exe"){RedirectStandardOutput = true});
if (process != null)
{
    process.OutputDataReceived += ((sender, e) =>
                                   {
                                       string consoleLine = e.Data;
                                       //handle data
                                   });
    process.BeginOutputReadLine();
}
櫻之舞 2024-09-10 10:34:04

编辑:刚刚意识到我回答了错误的问题。就我而言,标准输出缓冲区已满,并且 WaitForExit() 永远阻塞,因为尚未从缓冲区读取任何内容。因此,如果您遇到这个问题,那么这里有一个解决方案。 ;)

这是我使用 C# 的第一天,所以请理解这可能不是最好的解决方案,并且可能并不总是有效。但它在我测试过的 2x 中有效。 ;) 这是同步的,只需在 WaitForExit() 之前开始将重定向的 stdout/stderr 写入文件即可。这样,WaitForExit() 就不会阻塞等待标准输出缓冲区被清空。

      string str_MyProg = "my.exe";
      string str_CommandArgs = " arg1 arg2"'
      System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(str_MyProg, str_CommandArgs);

      procStartInfo.RedirectStandardError = true;
      procStartInfo.RedirectStandardOutput = true; // Set true to redirect the process stdout to the Process.StandardOutput StreamReader
      procStartInfo.UseShellExecute = false;
      procStartInfo.CreateNoWindow = true;          // Do not create the black window

      // Create a process, assign its ProcessStartInfo and start it
      System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
      myProcess.StartInfo = procStartInfo;
      myProcess.Start();

      // Dump the output to the log file
      string stdOut = myProcess.StandardOutput.ReadToEnd();
      StreamWriter logFile = new StreamWriter("output.txt" );
      logFile.Write(stdOut);
      logFile.Close();

      myProcess.WaitForExit();          

Edit: Just realized I'm answering the wrong question. In my case the stdout buffer was full and WaitForExit() was blocking forever, because nothing was reading from the buffer yet. So if you have THAT problem, then here's a solution. ;)

This is my first day with C# so please understand that this might not be the best solution, and might not always work. But it works in the 2x I've tested it. ;) This is synchronous, just start start writing the redirected stdout/stderr to the file before you WaitForExit(). This way WaitForExit() won't block waiting for the stdout buffer to be emptied.

      string str_MyProg = "my.exe";
      string str_CommandArgs = " arg1 arg2"'
      System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(str_MyProg, str_CommandArgs);

      procStartInfo.RedirectStandardError = true;
      procStartInfo.RedirectStandardOutput = true; // Set true to redirect the process stdout to the Process.StandardOutput StreamReader
      procStartInfo.UseShellExecute = false;
      procStartInfo.CreateNoWindow = true;          // Do not create the black window

      // Create a process, assign its ProcessStartInfo and start it
      System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
      myProcess.StartInfo = procStartInfo;
      myProcess.Start();

      // Dump the output to the log file
      string stdOut = myProcess.StandardOutput.ReadToEnd();
      StreamWriter logFile = new StreamWriter("output.txt" );
      logFile.Write(stdOut);
      logFile.Close();

      myProcess.WaitForExit();          
何必那么矫情 2024-09-10 10:34:04

是的,差不多是这样。 有一个用于存储进程输出的缓冲区,在常见的 CRT 实现中通常在 1 到 4KB 之间。一个小细节:该缓冲区位于您启动的进程中,而不是 .NET 程序中。

当您重定向到文件时,不需要发生任何特别的事情,CRT 直接写入它。但是,如果您重定向到 .NET 程序,则输出将从缓冲区进入管道。然后将线程切换到您的程序,以便您可以清空管道。来回好700次了。

是的,不快。不过很容易修复,在您正在运行的程序中调用 setvbuf() 来增加 stdout 和 stderr 输出缓冲区大小。话又说回来,这需要拥有该程序的源代码。

预计会出现问题:也许您应该使用 cmd.exe /c 重定向到文件,然后读取该文件。

Yes, that's about right. There is a buffer that stores the process output, usually between 1 and 4KB in the common CRT implementations. One small detail: that buffer is located in the process you start, not the .NET program.

Nothing very special needs to happen when you redirect to a file, the CRT directly writes it. But if you redirect to your .NET program then output goes from the buffer into a pipe. Which then takes a thread switch to your program so you can empty the pipe. Back and forth a good 700 times.

Yes, not fast. Easily fixed though, call setvbuf() in the program you are running to increase the stdout and stderr output buffer sizes. Then again, that takes having the source code of that program.

Anticipating a problem with that: maybe you ought to use cmd.exe /c to get the redirection to a file, then read the file.

悟红尘 2024-09-10 10:34:04

Process 类直接公开标准输出流,因此您应该能够以您喜欢的任何速度读取它。最好以小块的形式读取它并避免调用 ReadToEnd。

例如:

using(StreamReader sr = new StreamReader(myProcess.StandardOutput))
{
  string line;
  while((line = sr.ReadLine()) != null)
  {
    // do something with line
  }
}

The Process class exposes the stdout stream directly, so you should be able to read it at whatever pace you like. It's probably best to read it in small chunks and avoid calling ReadToEnd.

For example:

using(StreamReader sr = new StreamReader(myProcess.StandardOutput))
{
  string line;
  while((line = sr.ReadLine()) != null)
  {
    // do something with line
  }
}
娇俏 2024-09-10 10:34:04

这对我来说很有效:

            var sb = new StringBuilder();
            while (!proc.StandardOutput.EndOfStream)
            {
                sb.Append(proc.StandardOutput.ReadToEnd());
                proc.StandardOutput.DiscardBufferedData();
            }

This worked out for me:

            var sb = new StringBuilder();
            while (!proc.StandardOutput.EndOfStream)
            {
                sb.Append(proc.StandardOutput.ReadToEnd());
                proc.StandardOutput.DiscardBufferedData();
            }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文