Waitforexit hangs


Faz mais de um ano desde a última vez que bloguei. Desculpe por isso, mas foi um ano movimentado. Decidi retomar e tentar publicar alguns pensamentos aleatórios relacionados à tecnologia de vez em quando. Hoje eu só queria compartilhar um bug que descobri que poderia ser interessante para alguns de vocês. Eu tive uma pequena função que iniciou um processo e esperou que ele terminasse seu trabalho. O código parecia algo assim: Processo P novo Processo () P. StartInfo. UseShellExecute falso P. StartInfo. RedirectStandardOutput true P. StartInfo. RedirectStandardError true P. StartInfo. FileName pathToProgram P. StartInfo. Arguments programArguments P. Start () P. WaitForExit () P. Fechar () Uma vez que o processo às vezes demora um pouco para terminar, queria ver o que estava acontecendo, então eu decidi ler do stderrstdinput para ver se algo falhou. Para fazer isso, certifiquei-me de que as propriedades RedirectStandardError do processo foram definidas como verdadeiras (veja o código acima) e inseriu a seguinte linha para verificar se o aplicativo encontrou algum erro: P. WaitForExit () string err P. StandardError. ReadToEnd () P. Close () Depois que este código foi usado por um tempo, notei que, de vez em quando, o aplicativo estava pendurado ao executar o processo acima. O problema ocorreu apenas quando muitos dados foram gravados no standarderror. Deixei de resolver o problema e descobri que a omissão da leitura do StandardError eliminou o problema. Eu pensei que isso parecia um problema estranho, e continuou a procurar uma solução. Depois de ler a documentação, finalmente encontrei a informação que revelou o problema. A seguinte observação na biblioteca msdn para a propriedade RedirectStandardError diz: O componente Processo se comunica com um processo filho usando um pipe. Se um processo filho escrever dados suficientes para o pipe para preencher o buffer, a criança irá bloquear até que o pai lê os dados do tubo. Isso pode causar bloqueio se sua aplicação estiver lendo toda a saída para erro padrão e saída padrão, por exemplo, usando o seguinte código C. Então, em vez de pedir o processo para preencher o buffer e aguarde que eu o esvazie para que a chamada para WaitForExit () bloqueie, lendo o conteúdo do buffer (P. StandardError. ReadToEnd ()) antes de chamar WaitForExit () resolvido o Problema e fez da solução um sucesso. Conclusão: Leia a documentação. E certifique-se de ler as observações. O código parece quase isso: como você pode ver, o código inicia um processo cmd. exe e passa para ele o comando que quero ser executado. Eu redireciono StandardError e StandarOutput para lê-los a partir do código. O código lê-los antes do processo. WaitForExit (Timeout) chamada conforme recomendado pela Microsoft (mais sobre isso mais tarde). O problema surge se o comando que envie para cmd. exe nunca termina ou trava indefinidamente. No código eu usei o comando ping - t 8.8.8.8 que, por causa da opção - t, pings o host sem parar. O que acontece O processo cmd. exe junto com o comando ping - t nunca sai e nunca fecha o fluxo stdout e, portanto, o nosso código trava na linha OutputStandardOutput. ReadToEnd () porque não conseguiu ler todo o fluxo. O mesmo acontece também se um comando em um arquivo em lotes trava por qualquer motivo e, portanto, o código acima pode funcionar continuamente durante anos e, em seguida, é suspenso repentinamente sem nenhum motivo aparente. Antes de eu escrever, recomendava ler fluxos redirecionados antes do processo. WaitForExit (Timeout), bem, isso é especialmente verdadeiro se você usar a assinatura WaitForExit sem o tempo limite. Se você chamar processo. WaitForExit () antes de ler os fluxos redirecionados: código 2: você pode enfrentar um impasse se o comando que você anexar ao cmd. exe ou o processo que você está chamando preenche o padrão de saída ou erro padrão. Isso porque nosso código não pode atingir as linhas de processo de saída. StandardOutput. ReadToEnd () De fato, o processo filho (o comando ping ou um arquivo em lote ou o processo que estiver executando) não pode continuar se o nosso programa não lê os buffers preenchidos dos fluxos e isso não pode acontecer porque o código está pendurado em A linha com o processo. WaitForExit () que aguardará para sempre que o projeto filho saia. O tamanho padrão de ambos os fluxos é de 4096 bytes. Você pode testar esses dois tamanhos com esses arquivos em lote: o primeiro script grava 4096 bytes para a saída padrão e o segundo para o erro padrão. Salve um destes em C: testbuffsize. bat e execute nosso processo de chamada de programa. WaitForExit () antes do processo de saída. StandardOutput. ReadToEnd () como no código 2. Você pode fazê-lo escrevendo CommandResult Resultado ExecuteShellCommandSync (c: testbuffsize. bat, 1000) na linha 13 do código 1. O código não vai pendurar, mas se você escrever um byte mais em qualquer um dos dois fluxos, ele irá transbordar o tamanho do buffer tornando o programa aguentar. Se você precisa redirecionar e ler a saída padrão ou o erro padrão, a melhor solução é lê-los de forma assíncrona. Uma excelente maneira de fazer isso é proposta por Mark Byers neste segmento do stackoverflow. Como a última coisa, observe que, se o processo filho sair apenas porque você usa o processo. WaitForExit (Timeout) assinatura e ele realmente vai no tempo limite, você deve matar o processo cmd. exe e seus possíveis filhos.

Comments