.NET Process.Startで実行するとプロセスがハングする—どうしましたか?
-
22-07-2019 - |
質問
svn.exeの周りに迅速でダーティなラッパーを作成して、コンテンツを取得して処理しましたが、特定の入力に対しては時々再生可能にハングし、終了しません。たとえば、1つの呼び出しは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の提案に感謝します。私はマルチスレッド初心者なので、これを処理する方法について別の質問があります。明白な欠陥やその他の愚かなことを改善するための提案をお願いします。最終的に、stdoutストリーム、出力を保持する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;
}
}
これは機能しているように見えますが、これが最良の方法であるかどうかは疑問です。これは合理的ですか?そして、それを改善するために何ができますか?
解決
1つの標準的な問題:プロセスは、出力を読み取るのを待っている可能性があります。別のスレッドを作成して、終了するのを待っている間に標準出力から読み取ります。少し苦痛ですが、それが問題になる可能性があります。
他のヒント
Jon Skeetはお金を賭けています!
svnコマンドを起動した後にポーリングを気にしない場合は、これを試してください:
Process command = new Process();
command.EnableRaisingEvents = false;
command.StartInfo.FileName = "svn.exe";
command.StartInfo.Arguments = "your svn arguments here";
command.StartInfo.UseShellExecute = false;
command.StartInfo.RedirectStandardOutput = true;
command.Start();
while (!command.StandardOutput.EndOfStream)
{
Console.WriteLine(command.StandardOutput.ReadLine());
}
これは古い投稿であることは知っていますが、おそらくこれは誰かを助けるでしょう。これを使用して、.Net TPLタスクを使用していくつかのAWS (Amazon Web Services) CLIコマンドを実行しました。
WinFormバックグラウンドワーカー bgwRun_DoWork
メソッド内で作成され、 while(!bgwRun .CancellationPending)
。これには、.Net ThreadPoolクラスを使用して、新しいスレッドを介したプロセスからの標準出力の読み取りが含まれます。
private void bgwRun_DoWork(object sender, DoWorkEventArgs e)
{
while (!bgwRun.CancellationPending)
{
//build TPL Tasks
var tasks = new List<Task>();
//work to add tasks here
tasks.Add(new Task(()=>{
//build .Net ProcessInfo, Process and start Process here
ThreadPool.QueueUserWorkItem(state =>
{
while (!process.StandardOutput.EndOfStream)
{
var output = process.StandardOutput.ReadLine();
if (!string.IsNullOrEmpty(output))
{
bgwRun_ProgressChanged(this, new ProgressChangedEventArgs(0, new ExecutionInfo
{
Type = "ExecutionInfo",
Text = output,
Configuration = s3SyncConfiguration
}));
}
if (cancellationToken.GetValueOrDefault().IsCancellationRequested)
{
break;
}
}
});
});//work Task
//loop through and start tasks here and handle completed tasks
} //end while
}
SVNレポジトリが時々遅くなることがあるので、5秒では足りないかもしれません。ブレークポイントからプロセスに渡す文字列をコピーしたことがあるので、何も入力を求められないことがわかりますか?