Как я могу работать с выводом вторичного процесса в реальном времени?

Я пытаюсь написать программу VB .NET, которая запускает сценарий Perl, читает и использует стандартный вывод. Я хотел бы иметь возможность обрабатывать каждую строку отдельно по мере ее печати и соответствующим образом обновлять отображение моей программы. Вот некоторый код, который я «написал» (читай: «в основном скопирован из Интернета»):

Private WithEvents pscript As Process

Private Sub myProgram_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    pscript = New Process()
    pscript.StartInfo.CreateNoWindow = True
    pscript.StartInfo.FileName = "C:\perl64\bin\perl.exe"
    pscript.StartInfo.Arguments = "C:\test.pl"
    pscript.StartInfo.UseShellExecute = False
    pscript.StartInfo.RedirectStandardOutput = True
    pscript.StartInfo.RedirectStandardInput = True
    AddHandler pscript.OutputDataReceived, AddressOf pscript_output_process
    pscript.Start()
    pscript.BeginOutputReadLine()
End Sub

Private Sub pscript_output_process(sender As Object, e As DataReceivedEventArgs)
    MessageBox.Show(e.Data)
End Sub

Это работает с одной проблемой: программа ждет, пока тестовый сценарий не завершится, а затем запускает несколько событий OutputDataReceived одно за другим. Это означает, что когда я заставлю его использовать настоящий сценарий, он, скорее всего, ничего не сделает в течение нескольких часов, а затем ему придется иметь дело примерно с 5000 событий одновременно, несмотря на то, что материал печатался через довольно регулярные промежутки времени. на протяжении всего этого времени.

Есть ли способ заставить его обрабатывать каждую строку текста, когда он написан, а не все сразу в конце?


person gorcq    schedule 27.04.2012    source источник
comment
Это, вероятно, ни здесь, ни там, но в справочной документации неоднократно говорится, что приложение, обрабатывающее асинхронный вывод, должно вызывать метод WaitForExit, чтобы гарантировать, что выходной буфер был очищен.   -  person ssis_ssiSucks    schedule 28.04.2012
comment
Джим: Вызов WaitForExit приводит к тому, что приложение полностью зависает до тех пор, пока скрипт не будет выполнен, а в остальном все работает так же.   -  person gorcq    schedule 28.04.2012


Ответы (2)


Я понял, как это исправить, скопировав больше материала из Интернета. Проблема, похоже, была в Perl, который обычно не сбрасывает стандартный вывод после каждого оператора печати. Я действительно не знаю, что означает это последнее предложение, но в любом случае добавление строки local $| = 1; в начало скрипта (что, согласно одному из прочитанных мной сайтов, автоматически очищает стандартный вывод) устраняет проблему.

person gorcq    schedule 28.04.2012
comment
Стандартный вывод Flush — это немного небрежное описание. Он должен сказать очистить буфер для STDOUT. Для эффективности по умолчанию используется быстрый буфер RAM; данные помещаются в поток (файл, терминал, сокет) только тогда, когда буфер заполнен или очищается вручную (сбрасывается). Параметр $| отключает буферизацию. (Мое объяснение немного неточное, но суть верна; здесь недостаточно места, чтобы упомянуть более мелкие детали.) Прочтите Страдаете от буферизации? для расширенного обсуждения. - person daxim; 28.04.2012
comment
Спасибо за статью. Теперь это имеет гораздо больше смысла. - person gorcq; 28.04.2012

person    schedule
comment
По какой-то причине это ловит только первую напечатанную строку и все еще ждет, пока скрипт не будет выполнен. Кроме того, это приводит к зависанию моей программы во время выполнения скрипта, поэтому мне нравится метод, основанный на событиях (если он работает!). - person gorcq; 28.04.2012