У меня нет ответа на первый вопрос.Это более фундаментальная деталь проектирования или реализации Windows, и я не могу сказать, почему они решили сделать это таким образом.
Что касается второго вопроса ...
К сожалению, это кажетсябыть ограничением класса Process
из-за способа его реализации.
Использование асинхронного чтения из потоков stdout и stderr решает проблему WaitForExit()
, но ничего не решаетосновной способ, которым Windows связывает родительский процесс с дочерним процессом.Выходные потоки родителя (которые перенаправлены) не будут закрыты до тех пор, пока дочерний процесс не завершится, и поэтому в программе на C # все еще ожидают чтения родительских потоков вывода, ожидающих закрытия этих потоков.
В классе Process
для перенаправленного вывода он оборачивает дескриптор потока ввода-вывода в объект FileStream
, а при создании этого объекта не создает его с флагом async
, установленным в true
, который требуется, чтобы объект использовал IOCP для асинхронных операций.Он даже не создает базовый нативный объект канала с поддержкой IOCP.
Таким образом, когда код выполняет асинхронное чтение в потоке, это реализуется в .NET посредством «фальсифицированной» асинхронности.То есть он просто ставит в очередь синхронное чтение в обычном пуле потоков.
Таким образом, вы получаете новый поток активного пула потоков для каждого ожидающего чтения, по два на процесс.И последние чтения из выходных потоков не будут возвращаться, пока дочерний процесс не завершится, связав эти потоки пула потоков.
Я не вижу никакого отличного способа избежать этого.
Дляпроцесс, который вы знаете будет иметь очень маленькое количество вывода в потоках stdout и stderr (или вообще ни одного ... в данном примере, вообще ничего не записывается в stderr, например), вымог просто не читать потоки.Но буфер для каждого из этих потоков не очень велик, поэтому невозможность их чтения обычно приводит к тому, что процесс в конечном итоге блокируется.
В некоторых особых случаях вы можете расположить вещи так, чтобы не читать изпотоки, когда вы знаете, что вы в конце.Например, в приведенном выше коде вы можете раскомментировать «Пакетный файл готов!»и завершите цикл ConsumeReader()
, когда увидите этот текст.Но это не решение, которое будет работать во многих случаях.Он будет полагаться на то, чтобы точно знать, что будет записано, по крайней мере для самой последней строки, потоков stdout и stderr.
Для чего это стоит, по крайней мере, я бы сказал, что вы не буквально утечки темы.Каждый поток на самом деле что-то делает, и каждый поток на самом деле, наконец, будет освобожден, как только связанный процесс завершится.И, честно говоря, в Windows поток стоит намного меньше, чем процесс, поэтому, если у вас достаточно дочерних процессов, запущенных одновременно, и вы беспокоитесь о количестве потоков в одном процессе, вам, вероятно, придется жарить больше рыбы (т.е. все эти дочерние процессы поглощают ресурсы).
После исследования того, что я мог, я думаю, что ваше текущее решение, инструмент, используемый вместо команды start
для создания вашего процесса как отдельного потомка,наверное, самый элегантный.Единственной надежной альтернативой было бы эффективное повторное внедрение самого класса Process
(или, по крайней мере, частей, которые вы здесь используете), за исключением поддержки IOCP, так что чтение в стандартных потоках фактически осуществляется асинхронно.
Полагаю, вы могли бы попытаться сообщить об этой проблеме в Microsoft, но, думаю, на данный момент они не заинтересованы в каких-либо изменениях в классе Process
.