Tuesday 24 October 2017

Beginoutputreadline waitforexit doesnt wait


NET System. Diagnostics. Process Classe 8211 Parte 1 Process. WaitForExit e evento. Exited aren8217t trabalho Eu pensei que tinha encontrado este para ser o caso, mas foi minha culpa, provavelmente o mesmo para você também. Vou examinar o que encontrei ao explorar e resolver este problema. Resposta curta: Se você estiver redirecionando StandardOutput e / ou StandardError, use os métodos assíncronos Process. BeginErrorReadLine () e. BeginOutputReadLine () ANTES de chamar. WaitForExit () e capturar a saída conectando os eventos Process. ErrorDataReceived e. OutputDataReceived. A resposta longa começa comigo usando o Visual Studio diffmerge. exe na pasta Common7 / IDE para comparar arquivos de texto em modo batch. Estou introduzindo um teste de regressão em um processo de lote iniciado pela compilação. Eu precisava de uma ferramenta que cuspiria um arquivo de diferença de texto ao comparar dois arquivos (não um arquivo de resultado de mesclagem). WinMerge e Beyond Compare estão à minha disposição, mas eles não parecem produzir nada, mas resultados mesclados (que normalmente é o que eu quero, mas não desta vez). Minha estrutura de regressão chamará diffmerge. exe e armazenará o arquivo diff resultante para posterior revisão. Eu codifiquei o meu ProcessStartInfo Seguido que até com o pontapé de saída do processo e aguardando o processo para terminar. E espera espera esperando. Isso me fez ler o MSDN e aprofundar o uso da classe Process. Eu descobri algumas informações interessantes, provavelmente deveria ter sido óbvio. Primeiro, eu achei que, às vezes, executando o meu processo difmerge criança com diferentes argumentos funcionou, às vezes não, tornando a questão misteriosa. Em segundo lugar, descobri que funcionou bem quando eu não redirecionava a saída. Então, obviamente eu estava faltando alguma coisa. Eu precisava realmente ler os documentos de API de processo e, assim, eu encontrei esta pepita: Artigo MSDN Depois de encontrar e ler esse artigo MSDN eu entendi. Meu exemplo de código acima funcionará se o buffer StdOut ou StdError não preencher. No entanto, o que eu estava vendo era o buffer StdOut preenchendo, o processo filho foi bloqueado na próxima StdOut / StdError escrever, o processo pai estava esperando infinitamente no processo filho para sair antes de ler do buffer StdOut / StdError. Para mim, parecia que WaitForExit método e / ou evento Exited são quebrados / não captura o processo filho sair, no entanto, era o meu código que estava quebrado. Eu modifiquei o código para usar os métodos assíncronos e, de repente, meus problemas desapareceram. Não mais bloqueio, tudo funcionou como esperado. Eu usei StringBuilders como buffers para armazenar os dados recebidos nos eventos. Na parte 2, eu corro em um problema com o Process StdOut / StdError ReadLine implementações em torno de minhas necessidades específicas, eu entro em como eu resolvi que issue. I uso Process. Start para iniciar um arquivo em lotes. O arquivo em lotes usa o comando START para iniciar vários programas em paralelo e, em seguida, sai. Uma vez que o arquivo de lote é feito Process. HasExited torna-se verdadeira e Process. ExitCode contém o código de saída correto. Mas quando chamo Process. WaitForExit () ele trava / nunca retorna. O código a seguir demonstra o problema. Ele cria um arquivo em lotes, inicia-lo e imprime: Ele deve imprimir: mas ele nunca faz (mesmo que HasExited é verdadeiro e já temos um ExitCode). Notei que isso só acontece quando o arquivo em lotes contém comandos START e quando a saída padrão e / ou erro padrão são redirecionados. Por que WaitForExit () nunca retorna Qual é a maneira certa de esperar por esse processo para sair É seguro para apenas Poll Process. HasExited ou pode resultar em outros problemas PS. Eu só notei que chamar WaitForExit (100000) com um limite de tempo enorme (que definitivamente não expira) retorna imediatamente quando o processo sai. Estranho Sem tempo limite ele trava. Há uma diferença fundamental quando você chamar WaitForExit () sem um tempo limite, ele garante que o stdout / err redirecionados retornaram EOF. Isso garante que você leu toda a saída produzida pelo processo. Nós não podemos ver o que quotonOutputquot faz, mas altas chances de que deadlocks seu programa porque faz algo desagradável como assumindo que seu thread principal é ocioso quando ele é realmente preso em WaitForExit (). Ndash Hans Passant Nov 3 14 at 12:06 Este parece ser um artefato (id dizer bug) na implementação específica do manipulação assíncrona baseada em eventos de StandardOutput e StandardError. Percebi que enquanto eu era capaz de reproduzir facilmente o seu problema, simplesmente executando o código fornecido (excelente exemplo de código, por sinal.)), O processo não realmente pendurar indefinidamente. Em vez disso, ele retornou de WaitForExit () depois que ambos os processos filho que tinham sido iniciados tinham eles mesmos saído. Isso parece ser uma parte intencional da implementação da classe Process. Em particular, no método Process. WaitForExit (), uma vez que tenha terminado esperando no identificador de processo próprio, verifica para ver se um leitor para stdout ou stderr foi criado se assim, e se o valor de tempo limite para o WaitForExit ) É infinita (ie -1), o código realmente aguarda o fim-de-fluxo no leitor (s). Cada leitor respectivo é criado apenas quando o método BeginOutputReadLine () ou BeginErrorReadLine () é chamado. Os fluxos stdout e stderr não são fechados até que os processos filhos tenham fechado. Assim, esperar no final daqueles córregos irá bloquear até que isso aconteça. Que WaitForExit () deve se comportar de forma diferente dependendo se um chamou qualquer um dos métodos que iniciar a leitura baseada em eventos dos fluxos ou não, e especialmente dado que a leitura desses fluxos diretamente não faz com que WaitForExit () se comportam dessa maneira, cria Uma inconsistência na API que torna muito mais difícil de entender e usar. Embora Id pessoalmente chamar isso de um bug, eu suponho que é possível que o implementor (s) da classe Process estão cientes desta inconsistência e criado de propósito. Em qualquer caso, o work-around seria ler StandardOutput e StandardError diretamente em vez de usar a parte baseada em eventos da API. (Embora, claro, se os códigos fossem esperar nesses streams, ver-se-ia o mesmo comportamento de bloqueio até que os processos filhos fecham.) Por exemplo (C, porque eu não sei F bem o suficiente para bofetar um exemplo de código como este juntos rapidamente :)): Espero que o trabalho acima-around ou algo semelhante irá abordar a questão básica youve correr em. Meus agradecimentos ao comentarista Niels Vorgaard Christensen por me direcionar as linhas problemáticas no método WaitForExit (), para que eu pudesse melhorar essa resposta. Eu tenho o seguinte código: Eu sei que a saída do processo que estou começando é de cerca de 7MB de comprimento . Executá-lo no console do Windows funciona bem. Infelizmente, programaticamente isso trava indefinidamente em WaitForExit. Observe também este não código pendurar para saídas menores (como 3KB). É possível que o interno StandardOutput em ProcessStartInfo não pode buffer 7MB Se sim, o que devo fazer em vez Se não, o que estou fazendo errado respostas O problema é que se você redirecionar StandardOutput e / ou StandardError o buffer interno pode ficar cheio. Qualquer ordem que você usar, pode haver um problema: Se você esperar para o processo de sair antes de ler StandardOutput o processo pode bloquear tentando escrever para ele, para que o processo nunca termina. Se você ler de StandardOutput usando ReadToEnd, em seguida, seu processo pode bloquear se o processo nunca fecha StandardOutput (por exemplo, se ele nunca termina, ou se ele está bloqueado escrevendo para StandardError). A solução é usar leituras assíncronas para garantir que o buffer não fique cheio. Para evitar qualquer deadlocks e coletar todas as saídas de StandardOutput e StandardError você pode fazer isso: EDIT: Consulte as respostas abaixo para lidar com o tempo limite e evitar exceções ObjectDisposeException. A documentação para Process. StandardOutput diz para ler antes de esperar, caso contrário, você pode deadlock, snippet copiado abaixo: Mark Byers resposta é excelente, mas gostaria apenas de acrescentar o seguinte: o OutputDataReceived e ErrorDataReceived delegados precisam ser removidos antes do outputWaitHandle e errorWaitHandle get Disposto. Se o processo continuar a produzir dados após o tempo limite ter sido excedido e então terminar, as variáveis ​​outputWaitHandle e errorWaitHandle serão acessadas após serem descartadas. (FYI eu tive que adicionar esta advertência como uma resposta como eu não poderia comentar em seu borne.) Nós temos esta edição também (ou uma variante). Tente o seguinte: 1) Adicione um tempo limite para p. WaitForExit (nnnn) onde nnnn está em milissegundos. 2) Coloque a chamada ReadToEnd antes da chamada WaitForExit. Isto é o que nós vimos MS recomendar.

No comments:

Post a Comment