我在 svn.exe 周围编写了一个快速而肮脏的包装器来检索一些内容并对其进行处理,但对于某些输入,它偶尔且可重现地挂起并且无法完成。例如,一个调用是 svn list:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
当我只是从命令外壳执行此命令行时,它运行良好,但它挂在我的应用程序中。我运行它的 c# 代码是:
string cmd = "svn.exe";
string arguments = "list \"http://myserver:84/svn/Documents/Instruments/\" --xml --no-auth-cache --username myuser --password mypassword";
int ms = 5000;
ProcessStartInfo psi = new ProcessStartInfo(cmd);
psi.Arguments = arguments;
psi.RedirectStandardOutput = true;
psi.WindowStyle = ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
Process proc = Process.Start(psi);
StreamReader output = new StreamReader(proc.StandardOutput.BaseStream, Encoding.UTF8);
proc.WaitForExit(ms);
if (proc.HasExited)
{
return output.ReadToEnd();
}
这需要完整的 5000 毫秒并且永远不会完成。延长时间没有用。在单独的命令提示符下,它会立即运行,所以我很确定它与等待时间不足无关。然而,对于其他输入,这似乎工作正常。
我还尝试在这里运行一个单独的 cmd.exe(其中 exe 是 svn.exe 并且 args 是原始 arg 字符串),但仍然发生挂起:
string cmd = "cmd";
string arguments = "/S /C \"" + exe + " " + args + "\"";
我可以在这里搞砸什么,我该如何调试这个外部进程的东西?
编辑:
我现在才开始解决这个问题。Mucho 感谢 Jon Skeet 的建议,这确实很有效。不过,我对我的处理方法还有另一个问题,因为我是一个多线程新手。我想就改善任何明显的缺陷或任何其他愚蠢的东西提出建议。我最终创建了一个小类,其中包含标准输出流、一个保存输出的 StringBuilder 和一个告诉它何时完成的标志。然后我使用了 ThreadPool.QueueUserWorkItem 并传入了我的类的一个实例:
ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream,
Encoding.UTF8);
ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler);
proc.WaitForExit(ms);
if (proc.HasExited)
{
bufferHandler.Stop();
return bufferHandler.ReadToEnd();
}
... 和 ...
private class ProcessBufferHandler
{
public Stream stream;
public StringBuilder sb;
public Encoding encoding;
public State state;
public enum State
{
Running,
Stopped
}
public ProcessBufferHandler(Stream stream, Encoding encoding)
{
this.stream = stream;
this.sb = new StringBuilder();
this.encoding = encoding;
state = State.Running;
}
public void ProcessBuffer()
{
sb.Append(new StreamReader(stream, encoding).ReadToEnd());
}
public string ReadToEnd()
{
return sb.ToString();
}
public void Stop()
{
state = State.Stopped;
}
}
这似乎可行,但我怀疑这是最好的方法。这合理吗?我能做些什么来改进它?