C # Запуск нескольких неблокирующих внешних программ параллельно - PullRequest
1 голос
/ 17 апреля 2011

Мне нужно запустить несколько экземпляров внешнего исполняемого файла из моего приложения.Среднее время выполнения этого исполняемого файла составляет около 3 минут.Я хочу перенаправить вывод этих процессов и обновить индикатор выполнения в моем графическом интерфейсе.Конечно, я не хочу ждать, пока они вернутся, прежде чем я смогу продолжать использовать мое приложение.

Я думаю, что я должен создать поток для каждого экземпляра и обновить индикатор выполнения, когда поток завершится.

Это правильный подход?

Кроме того, вы рекомендуете хороший ресурс / документацию, чтобы понять, как он работает?Я нашел только http://www.dotnetperls.com/threadpool.

edit: эти процессы основаны на сети, т. Е. Время выполнения может сильно различаться в зависимости от задержки / пропускной способности канала.

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

edit 2:

Спасибо за ваш вклад.Поскольку мне, возможно, придется запускать много процессов (до 20), и я не хочу насыщать пропускную способность, я буду запускать 5 параллельно максимум.Каждый раз, когда процесс завершается, я увеличиваю счетчик хода выполнения (для моего индикатора выполнения) и запускаю еще один, пока все они не будут завершены, используя:

Process p = new Process();
p.StartInfo.FileName = pathToApp;
p.EnableRaisingEvents = true;
p.Exited += OnCalibrationProcessExited;
p.Start();

private void OnCalibrationProcessExited(object sender, EventArgs e)
{
  runAnotherOne function
}

Это правильно или есть более элегантный способдля достижения этой цели ?Я не хочу, чтобы мое приложение было заблокировано во время выполнения, конечно.Для этого лучше использовать фоновых работников?

Ответы [ 5 ]

4 голосов
/ 17 апреля 2011

Вы должны использовать Process и ProcessStartInfo.Вам нужно установить ProcessStartInfo.UseShellExecute на false, ErrorDialog на false, RedirectStandardOutput на true (и, возможно, RedirectStandardError тоже).

Вам также понадобитсяпредоставить делегат объекту Process для обработки выходных данных, сгенерированных внешним процессом через OutputDataReceived (и, возможно, ErrorDataReceived).

Также вы можете установить делегат Exited, который будет вызыватьсявсякий раз, когда процесс завершается.

Пример:

ProcessStartInfo processInfo = new ProcessStartInfo("Write500Lines.exe");
processInfo.ErrorDialog = false;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;

Process proc = Process.Start(processInfo);
proc.ErrorDataReceived += (sender, errorLine) => { if (errorLine.Data != null) Trace.WriteLine(errorLine.Data); };
proc.OutputDataReceived += (sender, outputLine) => { if (outputLine.Data != null) Trace.WriteLine(outputLine.Data); };
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();

proc.WaitForExit();
1 голос
/ 17 апреля 2011

Просто ожидание окончания каждого потока перед обновлением индикатора выполнения ничего не происходит ... затем быстрый прыжок .. 3 раза.Вы также можете пропустить индикатор выполнения.

Правильный способ сделать это ИМХО - рассчитать общую работу, проделанную во всех трех процессах:

totalwork = time1 + time2 + time3

Теперь, если у вас есть несколько процессоров, это будет больше похоже на max (time1, time2, time3), но это нормально.Это представление работы.

Иметь общую переменную для работы.Каждый раз, когда процесс выполняет еще какую-то работу, обновляйте индикатор выполнения, вычисляя work-done + = my-work-increment.Прогресс - это просто работа / общая работа.

Это даст хорошие результаты независимо от того, работают ли потоки последовательно или параллельно.Поскольку вы не знаете, как все будет работать (у вас может быть процессор с одним процессором), это лучший подход.

0 голосов
/ 17 апреля 2011

Как насчет создания ObservableCollection TaskProgressInfo,
где TaskProgressInfo - это пользовательский класс, в который вы записываете свой прогресс.

Привязать представление списка WPF к этой коллекции, используя табличку с данными (с типом цели = TaskProgressInfo), чтобы отобразить индикатор выполнения для каждого элемента (задачи).

Создайте массив BackgroundWorkers, который запускает внешнее приложение и отслеживает его.
Каждый фоновый работник должен обновить свою TaskProgressInfo, тем самым обновляя источник данных индикатора выполнения.

После завершения каждый BackgroundWorker должен удалить свой TaskProgressInfo из ObservableCollection, тем самым удаляя индикатор выполнения из пользовательского интерфейса.

Поскольку BackgroundWorker использует фоновый поток (UI) для выполнения и завершения отчета, изменения в ObservableCollection будут сделаны его потоком создания (потокобезопасным).

За кулисами .NET будет использовать ThreadPool - некоторые фоновые работники будут делиться потоками.

0 голосов
/ 17 апреля 2011

Просто создайте несколько экземпляров класса Process, вызвав конструктор, установите свойства для перенаправления выходного потока, а затем запустите их.

Ваша программа не будет ждать завершения вызываемого процесса, покатак как вы не вызываете метод WaitForExit.Многопоточность не требуется.

0 голосов
/ 17 апреля 2011

Создать отдельный поток.

В этом потоке (псевдокод):

Thread begins here
for each externalApp
   Run the application with redirect output 
   Wait for exit
   Update progress bar
end for
Thread ends here

См. http://msdn.microsoft.com/en-us/library/ty0d8k56.aspx для ожидания выхода

Или ... хотите ли вы запускать внешние приложения параллельно?

Редактировать: на основе последних обновлений в оригинальном сообщении:

Если вы не знаете фактический прогресс, не используйте обычный индикатор выполнения.Как насчет бесконечного индикатора выполнения или «рабочего» значка?

Бесконечный индикатор выполнения может быть индикатором заполнения, который заполняется и начинается с начала, пока все не будет сделано.Рабочий значок похож на курсор занятости Windows (постоянно вращающийся круг).

...