2017-02-22 17 views
1

Это было своего рода «уголовное исследование» с очень удивительным результатом - по крайней мере, для меня.Как ждать выходных потоков, когда Process.WaitForExit используется с жестким таймаутом

Мы выполняем внешние команды с использованием объекта Process. Мы фиксируем стандартный вывод (и стандартную ошибку - я удалил это из образца для ясности) асинхронно. Об этом несколько раз ответили. Код выглядит так и работает отлично:

var process = new Process 
{ 
    StartInfo = 
    { 
     UseShellExecute = false, 
     RedirectStandardOutput = true, 
     FileName = @"...\TestApp.exe" 
    } 
}; 
process.OutputDataReceived += Process_OutputDataReceived; 
process.Start(); 
process.BeginOutputReadLine(); 
process.WaitForExit(); 
process.OutputDataReceived -= Process_OutputDataReceived; 

Теперь, если я хочу, чтобы добавить жесткий тайм-аут в WaitForExit()? Легко: есть перегруженная версия WaitForExit(int milliseconds). Таким образом, вызов, как это следует делать:

process.WaitForExit(60000); 

Теперь это делает выходной захват неполный в некоторых случаях.

Это не секрет, по крайней мере, не для разработчиков Microsoft. После нескольких часов исследований я пришел к выводу, что с реализацией WaitForExit что-то должно быть не так. Так что я посмотрел в код, и я нашел этот комментарий в реализации Microsoft:

// If we have a hard timeout, we cannot wait for the streams 

Вот мой вопрос - и я сделал некоторое усилие оценки без успеха:

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

+1

Не игнорировать возвращаемое значение WaitForExit(). Если это * true *, то вы знаете, что все еще может быть некоторый непрочитанный вывод. Так что спать немного, чтобы догнать его. –

+0

Сон - это именно то, что я пытаюсь устранить. [sarcasm on] По моему скромному мнению, 'Thread.Sleep()' должен автоматически всплывать окно с сообщением «Я плохой программист», который должен быть подтвержден каждый раз. (Нет, шучу, может быть 0.05% случаев, когда 'Thread.Sleep()' также используется хорошими программистами по уважительным причинам тоже.) [sarcasm off] – freefall

+0

Эта проблема началась давно, многократное перенаправление ввода-вывода, безусловно, было одним из Unix «худшие возможности, чтобы превратить его в Windows. Такие хаки просто не стареют. Не имея дело с взломом вывода stderr, это еще один, о котором вы, вероятно, пожалеете. Вам придется взломать хаки, вот что нужно. –

ответ

0

@Hans Passant Ваш комментарий направил меня в правильном направлении. На самом деле, я действительно не знаю, как долго до Sleep(). Но для этого я могу добавить простую собаку. Поскольку процесс уже завершен к моменту, когда я должен начать ждать недостающих захватов, я могу предположить, что событие OutputDataReceived запускается непрерывно, пока поток не будет пустым. Итак, все, что я сделал, это добавить Stopwatch и перезапустить его каждый раз в начале метода событий OutputDataReceived. В основном коде я заменить вызов WaitForExit(60000) следующим кодом:

if (process.WaitForExit(60000)) 
{ 
    while (outputWatch.ElapsedMilliseconds < 20) Thread.Sleep(20); 
} 

Хорошо, есть Thread.Sleep() внутри этого кода, а также. Но это определенно считается 0,05% ... :)

1

Вы пытались использовать событие OnExited в производном классе? See this. Это может позволить вам использовать тайм-аут ожидания самостоятельно, а не передавать его в WaitForExit.

+0

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

0

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

Это был дан ответ здесь: ProcessStartInfo hanging on "WaitForExit"? Why?

 Смежные вопросы

  • Нет связанных вопросов^_^