Зависание процесса при запуске с .NET Process.Start - что не так? - PullRequest
27 голосов
/ 13 января 2009

Я написал быструю и грязную оболочку вокруг svn.exe, чтобы извлечь некоторый контент и что-то с ним сделать, но для определенных входов он иногда и воспроизводимо зависает и не завершится. Например, один вызов в список svn:

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 + "\"";

Что я мог напортачить здесь, и как я могу отлаживать этот внешний процесс?

EDIT:

Я только сейчас приступаю к решению этой проблемы. Большое спасибо Джону Скиту за его предложение, которое действительно прекрасно работает. У меня есть еще один вопрос о моем методе обработки этого, так как я многопоточный новичок. Я хотел бы получить предложения по улучшению любых явных недостатков или чего-то еще глупого. Я закончил тем, что создал небольшой класс, который содержит поток 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;
    }
}

Кажется, это работает, но я сомневаюсь, что это лучший способ. Это разумно? И что я могу сделать, чтобы улучшить это?

Ответы [ 5 ]

37 голосов
/ 13 января 2009

Одна стандартная проблема: процесс может ждать, пока вы прочитаете его вывод. Создайте отдельный поток для чтения из его стандартного вывода, пока вы ожидаете его выхода. Это немного больно, но это может быть проблемой.

14 голосов
/ 15 июля 2009

Джон Скит прав на деньги!

Если вы не возражаете против опроса после запуска команды 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());
}
2 голосов
/ 13 августа 2018

Мне пришлось оставить exe на клиентском компьютере и использовать Process.Start для его запуска.

Вызывающее приложение зависнет - проблема в том, что их компьютер предположил, что исполняемый файл опасен, и не позволил другим приложениям запустить его.

Щелкните правой кнопкой мыши на exe и перейдите в свойства. Нажмите «Разблокировать» внизу рядом с предупреждением безопасности.

enter image description here

1 голос
/ 28 апреля 2016

Я знаю, что это старый пост, но, возможно, это кому-нибудь поможет. Я использовал это для выполнения некоторых команд CLI AWS (Amazon Web Services) с использованием задач .Net TPL.

Я сделал что-то подобное при выполнении моей команды, которая выполняется в рамках задачи .Net TPL, которая создается в моем фоновом рабочем методе 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
}
0 голосов
/ 13 января 2009

Я знаю, что мои репозитории SVN иногда могут работать медленно, так что, может быть, 5 секунд недостаточно? Скопировали ли вы строку, которую вы передаете процессу, из точки останова, так что вы уверены, что она вас ни о чем не просит?

...