c# - example - Proceso de ejecución cuando se ejecuta con.NET Process.Start: ¿qué ocurre?
system.diagnostics.process.start arguments (4)
Escribí un contenedor rápido y sucio alrededor de svn.exe para recuperar algo de contenido y hacer algo con él, pero para ciertas entradas ocasionalmente y reproduciblemente se cuelga y no termina. Por ejemplo, una llamada es svn list:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
Esta línea de comando funciona bien cuando solo lo hago desde un shell de comandos, pero se cuelga en mi aplicación. Mi código c # para ejecutar esto es:
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();
}
Esto lleva los 5000 ms completos y nunca termina. Extender el tiempo no ayuda. En un símbolo del sistema por separado, se ejecuta al instante, por lo que estoy bastante seguro de que no está relacionado con un tiempo de espera insuficiente. Para otras entradas, sin embargo, esto parece funcionar bien.
También intenté ejecutar un cmd.exe por separado aquí (donde exe es svn.exe y args es la cadena arg original), pero todavía se colgó:
string cmd = "cmd";
string arguments = "/S /C /"" + exe + " " + args + "/"";
¿Qué podría estar arruinando aquí, y cómo puedo depurar este proceso externo?
EDITAR:
Ahora estoy tratando de resolver esto. Mucho agradece a Jon Skeet por su sugerencia, que de hecho funciona muy bien. Sin embargo, tengo otra pregunta sobre mi método para manejar esto, dado que soy un novato con múltiples subprocesos. Me gustaría recibir sugerencias para mejorar cualquier deficiencia evidente o cualquier otra cosa tonta. Terminé creando una clase pequeña que contiene la corriente estándar, un StringBuilder para contener la salida y una bandera para indicar cuándo ha finalizado. Luego utilicé ThreadPool.QueueUserWorkItem y lo pasé en una instancia de mi clase:
ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream,
Encoding.UTF8);
ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler);
proc.WaitForExit(ms);
if (proc.HasExited)
{
bufferHandler.Stop();
return bufferHandler.ReadToEnd();
}
... y ...
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;
}
}
Esto parece funcionar, pero dudo que esta sea la mejor manera. Es esto razonable? ¿Y qué puedo hacer para mejorarlo?
Sé que mis repositorios SVN pueden correr lento a veces, ¿entonces quizás 5 segundos no son lo suficientemente largos? ¿Ha copiado la cadena que está pasando al proceso desde un punto de quiebre, por lo que está seguro de que no lo está impulsando?
Un problema estándar: el proceso podría estar esperando que usted lea su salida. Cree un hilo separado para leer desde su salida estándar mientras espera que salga. Es un poco doloroso, pero ese puede ser el problema.
¡Jon Skeet tiene razón con el dinero!
Si no le molesta la votación después de iniciar su comando svn, intente esto:
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());
}
Sé que esta es una publicación anterior, pero quizás esto ayude a alguien. Lo usé para ejecutar algunos comandos CLI de AWS (Amazon Web Services) usando tareas de .NET TPL.
Hice algo así en mi ejecución de comandos que se ejecuta dentro de una tarea .Net TPL que se crea dentro de mi método WinForm background worker bgwRun_DoWork
que mantiene un ciclo con while(!bgwRun.CancellationPending)
. Esto contiene la lectura de la salida estándar del proceso a través de un nuevo subproceso utilizando la clase .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
}