问候stackoverflow成员,
在WPF前端的BackgroundWorker中,我在System.Diagnostics.Process
中运行sox(开源控制台声音处理工具)。以同样的方式,我使用其他几个命令行工具,并解析他们的输出,以在我的前端poulate进度条。
这适用于其他工具,但不适用于Sox,因为它不是为每个进度步骤发送新行,而是通过仅使用回车符(\ r)和没有换行符来更新控制台上的单行(\ n) 。我在process.StandardError
上尝试了异步和同步读取。
将async process.ErrorDataReceived += (sender, args) => FadeAudioOutputHandler(clip, args);
与process.BeginErrorReadLine();
结合使用不会产生任何单独的状态更新,因为由于某种原因,回车不会触发ReadLine,即使MSDN docs表明它应该。当进程结束时,输出将在一个块中吐出。
然后我通过流上的char读取尝试了以下代码用于同步char:
char[] c;
var line = new StringBuilder();
while (process.StandardError.Peek() > -1)
{
c = new char[1];
process.StandardError.Read(c, 0, c.Length);
if (c[0] == '\r')
{
var percentage = 0;
var regex = new Regex(@"%\s([^\s]+)");
var match = regex.Match(line.ToString());
if (match.Success)
{
myProgressObject.ProgressType = ProgressType.FadingAudio
//... some calculations omitted for brevity
percentage = (int) Math.Round(result);
}
else
{
myProgressObject.ProgressType = ProgressType.UndefinedStep;
}
_backGroundWorker.ReportProgress(percentage, myProgressObject);
line.Clear();
}
else
{
line.Append(c[0]);
}
}
上面的代码似乎没有实时读取流,但会暂停输出一段时间。然后它淹没了一小块,最后在整个过程中途陷入僵局。
非常感谢任何向正确方向的提示!
UPDATE with(sloppy?)解决方案:
这让我抓狂,因为我在C#方面试过的东西似乎对结果没有任何影响。我的原始实现,在更改它15次并引入新的依赖项之前,很好。
问题出在sox和RedirectStandardError上。我发现在获取sox源代码并构建自己的版本之后。首先我完全删除了sox的所有输出,除了我真正感兴趣的东西,然后将输出更改为实线,然后换行符\n
。我认为这会解决我的问题。好吧,它没有。我不知道足够的c ++实际找出原因,但是他们似乎已经调整了stdio如何写入该流,如何缓冲它或以如此特殊的方式执行它,使得c#端的流读取器不会刷新,直到默认4096字节缓冲区已满。我确认通过将每行填充到至少4096字节。总而言之,我所要做的就是在fprintf(stderr, ...)
中每次display_status(...)
调用后手动刷新sox.c中的stderr:
fflush(stderr);
尽管如此,我不确定这是否接近优雅的解决方案。
感谢Erik Dietrich的回答让我从不同的角度看待这个问题。
答案 0 :(得分:9)
您描述的情况是一个已知问题 - 对于包含源代码的解决方案,请参阅http://www.codeproject.com/KB/threads/ReadProcessStdoutStderr.aspx
它解决了两个问题(死锁和\n
的问题)......
答案 1 :(得分:2)
我不得不在visual studio中使用定制的构建工具处理类似的问题。我发现使用正则表达式并在与读取相同的线程中进行解析是一个问题,输出处理将停止。我最终得到了一个标准的消费者生产者解决方案,您可以从输出中读取行并将它们粘贴到队列中。然后让队列出队并在其他线程上处理。我无法提供源代码,但此网站有一些很棒的资源:http://www.albahari.com/threading/part2.aspx
答案 2 :(得分:2)
这有点笨拙,但是也许你可以将不合作进程的输出传递给一个进程,该进程除了处理字符输入,插入换行符和写入标准输出之外什么都不做......所以,就(的而言)非常)伪代码:
StartProcess("sox | littleguythatIwrote")
ReadStandardOutTheWayYouAleadyAre()
可能只是移动球门柱(我对NIX世界中的std in / out / err更加熟悉),但无论如何,这是一种不同的方式来看待问题。