C #: чтение блоков stdout подпроцесса, пока ДРУГОЙ подпроцесс не завершится - PullRequest
0 голосов
/ 30 октября 2010

Вот код C #, который я использую для запуска подпроцесса и контроля его вывода:

using (process = new Process()) {
    process.StartInfo.FileName = executable;
    process.StartInfo.Arguments = args;
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardInput = true;
    process.StartInfo.CreateNoWindow = true;
    process.Start();

    using (StreamReader sr = process.StandardOutput) {
        string line = null;
        while ((line = sr.ReadLine()) != null) {
            processOutput(line);
        }
    }

    if (process.ExitCode == 0) {
        jobStatus.State = ActionState.CompletedNormally;
        jobStatus.Progress = 100;
    } else {
        jobStatus.State = ActionState.CompletedAbnormally;
    }
    OnStatusUpdated(jobStatus);
}

Я запускаю несколько подпроцессов в отдельных потоках ThreadPool (но не более четырех одновременно, начетырехъядерный станок).Это все работает нормально.

Проблема, с которой я столкнулся, заключается в том, что один из моих подпроцессов завершит свою работу, но соответствующий вызов sr.ReadLine() будет заблокирован, пока ДРУГОЙ один из моих подпроцессов не завершится.Я не уверен, что он возвращает, но это НЕ должно происходить, если я что-то упускаю.

В моем подпроцессе нет ничего, что могло бы их каким-либо образом "связать" - они нене общаются друг с другом.Я даже могу посмотреть в диспетчере задач / Process Explorer, когда это происходит, и увидеть, что мой подпроцесс фактически завершился, но вызов ReadLine() на его стандартном выходе все еще блокирует!

Я смогчтобы обойти это, выкрутив выходной код мониторинга в новый поток и выполнив process.WaitForExit(), но это выглядит как очень странное поведение.Кто-нибудь знает, что здесь происходит?

Ответы [ 2 ]

2 голосов
/ 30 октября 2010

В MSDN описаны ProcessStartInfo.RedirectStandardOutput подробные обсуждения взаимных блокировок, которые могут возникнуть при выполнении того, что вы делаете здесь. Предоставляется решение, которое использует ReadToEnd, но я полагаю, что тот же совет и средство защиты будут применяться при использовании ReadLine.

Введение в синхронные операции чтения зависимость между вызывающим чтение из потока StandardOutput и дочерний процесс, пишущий на что поток. Эти зависимости могут привести к тупиковые условия. Когда звонящий читает из перенаправленного потока дочерний процесс, это зависит от ребенок. Звонящий ждет чтения операция, пока ребенок не пишет в поток или закрывает поток. когда дочерний процесс записывает достаточно данных чтобы заполнить его перенаправленный поток, это зависит от родителя. Ребенок процесс ждет следующей записи операция, пока родитель не читает из полный поток или закрывает поток. Состояние тупика возникает, когда вызывающий и дочерний процесс ждут друг друга, чтобы завершить операцию, и ни один не может продолжаться. Вы можете избежать тупиков, оценивая зависимости между вызывающим и дочерний процесс.

Наилучшим решением является асинхронный ввод-вывод, а не методы синхронизации:

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

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

0 голосов
/ 30 октября 2010

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

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

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

...