Tuesday 14 November 2017

Waitforexit asynchronously


O código parece quase que este: Como você pode ver, o código inicia um processo cmd. exe e passa para ele o comando que eu quero ser executado. Eu redireccionar StandardError e StandarOutput para lê-los a partir do código. O código lê-los antes do processo. WaitForExit (Timeout) chamar conforme recomendado pela Microsoft (mais sobre isso mais tarde). O problema surge se o comando que eu enviar 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 assim o nosso código trava na linha Output. StandardOutput. ReadToEnd () porque não consegue ler todos os fluxos. O mesmo acontece também se um comando em um arquivo em lotes trava por qualquer motivo e, portanto, o código acima poderia trabalhar continuamente por anos e, em seguida, pendurar de repente sem qualquer razão aparente. Antes de eu escrever que seu recomendado para ler fluxos redirecionados antes do processo. WaitForExit (Timeout) chamada, bem isso é especialmente verdadeiro se você usar a assinatura WaitForExit sem o tempo limite. Se você chamar o processo. WaitForExit () antes de ler os fluxos redirecionados: código 2: você pode enfrentar um deadlock se o comando que você anexar ao cmd. exe ou o processo que você está chamando preenche a saída padrão ou erro padrão. Isso porque o nosso código não pode alcançar o processo de saída de linhas. StandardOutput. ReadToEnd () Como uma questão de fato o processo filho (o comando ping ou um arquivo em lotes ou qualquer processo que você está executando) não pode ir se o nosso programa não lê os buffers cheios dos fluxos e isso não pode acontecer porque o código está pendurado em A linha com o processo. WaitForExit () que irá esperar para sempre para o projecto filho para sair. O tamanho padrão de ambos os streams é 4096 bytes. Você pode testar esses dois tamanhos com esses arquivos em lote: O primeiro script grava 4096 bytes na saída padrão e o segundo no erro padrão. Salve um desses em C: testbuffsize. bat e execute o 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 cair, mas se você escrever mais um byte em qualquer um dos dois fluxos ele vai transbordar o tamanho do buffer fazendo o programa aguentar. Se você precisa redirecionar e ler a saída padrão ou erro standar a melhor solução é lê-los de forma assíncrona. Uma excelente maneira de fazer isso é proposta por Mark Byers neste thread stackoverflow Como a última coisa, observe que se o processo filho sai apenas porque você usa o processo. WaitForExit (Timeout) assinatura e ele realmente vai em timeout você deve matar o processo cmd. exe e seus filhos possíveis. Providing async funcionalidade para System. Diagnostics. Process Se você precisa interagir com processos em. NET, você poderia fazer pior do que System. Diagnostics. Process. Mas você também pode fazer um pouco melhor, especialmente desde que o processo não fornece out-of-the-box métodos assíncronos. Mas podemos corrigir que Let8217s conceber uma classe ProcessEx que fornece versões assíncronas dos métodos Start e WaitForExit. Aqui está a visão geral da classe: O construtor é privado, uma vez que só suportamos instanciar ProcessEx pelo método de fábrica StartAsync: Infelizmente, não há nenhuma API do Windows para iniciar um processo de forma assíncrona, então estamos presos descarregando o processo de bloqueio Process. Start para um thread de trabalho . Ok, então o que vamos fazer agora que temos as mãos em uma instância de processo Bem, precisamos garantir que EnableRaisingEvents é definido como true para que possamos ser notificados através do evento Exited quando o processo sai. Isso nos permitirá criar uma versão assíncrona simples do WaitForExit. Usando nosso velho amigo TaskCompletionSource. Aqui está o código relevante: Observe o uso de TrySetResult em vez de SetResult e a verificação explícita de HasExited. Isso é para lidar com duas condições de corrida em potencial: O evento Exited pode nunca ser levantado uma vez que o processo já poderia ter saído no momento em que nos inscrever. A verificação de HasExited pode ocorrer ao mesmo tempo que o manipulador OnProcessExited é chamado. Deixem o Dispose fora do caminho. Para evitar WaitForExitAsync de bloquear para sempre, we8217ll tentativa de completá-lo com um ObjectDisposedException: Agora precisamos implementar a sobrecarga de WaitForExitAsync que aceita um tempo limite: Basicamente, precisamos aguardar o resultado de duas operações, um rastreamento do tempo limite (tcs acima) E o outro processo de rastreamento sair (this. exited). A tarefa de tempo limite só será concluída se o tempo limite expirar antes de o processo realmente sai. Para gerenciar isso, usamos CancellationTokenSource. CancelAfter e um CancellationTokenRegistration que irá completar a tarefa de tempo limite. (Observe que se o CancellationToken já estiver em um estado cancelado, Register executará o retorno de chamada imediatamente, então não há corrida aqui.) Usamos Task. WhenAny para nos notificar quando uma das tarefas for concluída. (Uma vez que somos bons cidadãos, podemos descartar o registro após este ponto, já que don8217t precisa dele mais.) Se a primeira tarefa para concluir indica que o processo saiu, aguardamos a tarefa (no caso de ele completou com uma exceção) e retornar true . Caso contrário, o processo ainda está em execução, tanto quanto sabemos e devolvemos false. Isso é tudo o que há para ele. Aqui está um caso de uso simples de criar um arquivo, abrindo uma janela do Bloco de Notas para exibi-lo, e esperando o usuário para fechá-lo: 2 pensamentos sobre ldquo Fornecer funcionalidade async para System. Diagnostics. Process rdquoIt parecia-me que este sistema de asynchrony foi Uma solução gloriosa para um problema Ive ter onde eu preciso para iniciar um processo externo e, em seguida, esperar que ele seja concluído. Atualmente, eu tenho o seguinte código no thread da interface do usuário: Process process new Process () process. Start () process. WaitForExit () O processo geralmente leva vários segundos, mas eu resisti usando um trabalhador de plano de fundo ou similar porque simplesmente não acontece muitas vezes O suficiente para valer a pena o esforço e código-scrambling. Eu vi menção de métodos de extensão no whitepaper, mas não parece haver nenhum método de extensão para process. RunAsync () ou algo semelhante, deixando quotawait TaskEx. Run (() gt process. WaitForExit ()) quot como a única solução que parece Como itll trabalho. Girando um fio extra apenas para chamar WaitForExit parece bastante estúpido. Eu também não encontrar qualquer métodos estáticos que parecia promissor em qualquer de AsyncCtpExtensions, AsyncCtpThreadingExtensions, TaskEx ou TaskExtensions. Existe uma maneira melhor (planejada) para aguardar a conclusão do processo externo Friday, October 29, 2010 6:35 PM Respostas A desvantagem de usar a piscina desta maneira é que você estará bloqueando um segmento para a duração do processo. Se você não está girando acima de muitos processos, este não pode ser um problema enorme, mas theres ainda nenhuma necessidade real de fazer assim. Em vez disso, você pode usar uma tarefa que não está vinculada a um segmento, p. Tcs. TrySetResult (null) if (p. HasExited) tcs. TrySetResult (null) return tcs. Task Com isso, Você pode escrever: e você não estará bloqueando quaisquer threads enquanto espera assíncrona para o processo para sair. Postado como resposta por Stephen Toub - MSFT Funcionário da Microsoft, Moderador sexta-feira, 29 de outubro de 2010 21:29 Marcado como resposta por Lucian Wischik, MSFT Funcionário da Microsoft, Proprietário Sábado, 30 de outubro de 2010 22:13 Sexta-feira, 29 de outubro de 2010 9:28 PM quot Girando um fio extra apenas para chamar WaitForExit parece bastante estúpido. Se você usar a funcionalidade padrão TPL, por padrão, itll usar um segmento ThreadPool. Realmente não deve haver um monte de tempo de espera para quotspin upquot o segmento. Uma vez que isso é algo que não acontece muitas vezes o suficiente para valer a pena o esforço e codificar-scramblingquot, Id apenas usar: Reed Copsey, Jr. - reedcopsey Se um post responde a sua pergunta, por favor clique quot Mark As Resposta quot nesse post e quot Marcar como útil quot. Sexta-feira, 29 de outubro de 2010 19:30 A desvantagem de usar a piscina desta forma é que você estará bloqueando um segmento para a duração do processo. Se você não está girando acima de muitos processos, este não pode ser um problema enorme, mas theres ainda nenhuma necessidade real de fazer assim. Em vez disso, você pode usar uma tarefa que não está vinculada a um segmento, p. Tcs. TrySetResult (null) if (p. HasExited) tcs. TrySetResult (null) return tcs. Task Com isso, Você pode escrever: e você não estará bloqueando quaisquer threads enquanto espera assíncrona para o processo para sair. Postado como resposta por Stephen Toub - MSFT Funcionário da Microsoft, Moderador sexta-feira, 29 de outubro de 2010 21:29 Marcado como resposta por Lucian Wischik, MSFT Funcionário da Microsoft, Proprietário Sábado, 30 de outubro de 2010 22:13 Sexta-feira, 29 de outubro de 2010 21:28

No comments:

Post a Comment