Pergunta

C# .NET 3.5

Eu tenho um aplicativo de console que está sendo chamado por outro aplicativo no computador. Este aplicativo de console é executado continuamente e ouve dados sobre o processo "pai".

No entanto, quando o pai é interrompido ou morto, o aplicativo de console que começou continua continua. Em circunstâncias normais, fica e ociosos aguardando a contribuição de Stdin, usando recursos mínimos. No entanto, assim que o pai desaparece, este aplicativo de console aumenta a CPU e fome o núcleo em que está sendo executado com quase 100% de utilização. Isso continua até que eu mate manualmente o processo.

Idealmente, o pai chamado se limparia depois de si, principalmente porque isso está acontecendo em condições normais (não excepcionais) de "parar". Infelizmente, esse processo pai está fora de minhas mãos.

Meu primeiro pensamento foi pegar o pai invocando o aplicativo de console e monitorar seu PID. Se o processo pai desaparecer, o aplicativo do console se encerraria. Atualmente, estou fazendo isso por:

Process process = Process.GetCurrentProcess();
m_ParentPID = 0;
using (ManagementObject mgmtObj = new ManagementObject("win32_process.handle='" +     process.Id.ToString() + "'"))
{
    mgmtObj.Get();
    m_ParentPID = Convert.ToInt32(mgmtObj["ParentProcessId"]);
}
string parentProcessName = Process.GetProcessById(m_ParentPID).ProcessName;
Log("Parent Process: " + parentProcessName + Environment.NewLine);

// Create a timer for monitoring self.
Timer timer = new Timer(new TimerCallback(sender =>
{
    if (m_ParentPID != 0)
    {
        Process parent = System.Diagnostics.Process.GetProcessById(m_ParentPID);
        if (parent == null)
        {
            Log("Parent process stopped/killed.  Terminating self.");
            System.Environment.Exit(0);
        }
    }
}));

// Kick on the timer
timer.Change(m_ExitWatcherFrequency, m_ExitWatcherFrequency);

Porém, isso só funciona parcialmente - ele para o Spike da CPU, mas se eu olhar para meus processos do SysInternals Wonderful Process Monitor, posso ver o programa DW20.EXE - o programa "Microsoft Application Error Relating". E apenas ... fica lá, e o aplicativo Console permanece na memória.

O que devo fazer aqui para encerrar corretamente o processo para evitar esse pico contínuo da CPU e memória não lançada? Eventualmente, isso precisa estar funcionando sem intervenção.

PS Estou usando um aplicativo de linha de comando aqui como um "programa de longa execução" em vez de um serviço do Windows ou serviço da Web, porque o programa pai só pode ser configurado para executar um aplicativo de linha de comando para o qual passa dados via stdin. (Para aqueles que estão curiosos, isso é ejabberd, usando autenticação externa).

EDITAR:

O código que aguarda a entrada de Stdin é:

// Read data from stdin
char[] charray = new char[maxbuflen];
read = Console.In.Read(charray, 0, 2);

Mencionei anteriormente que, quando o pai termina, o aplicativo Console fica louco na CPU. Anexei um depurador a ele do Visual Studio e ainda está sentado nesse console de linha. Em teoria, então, quando o timer de auto-monitoramento desencadeia e vê o pai desaparecer, ele tenta um sistema.ENVERNAMENT.EXIT (0) Quando o outro thread está nessa linha read ().

Foi útil?

Solução

Parece que seu processo está entrando em um loop duro devido ao fechamento do fluxo de entrada do console quando o pai sai. Você está verificando o valor de retorno de Console.In.Read? Ele retornará zero quando o fluxo estiver fechado. Naquele ponto saia do loop e deixe seu Main() Método Sair por si só.

E se você estiver executando vários tópicos, eles terão que terminar primeiro, usando Thread.Join ou equivalente.

Outras dicas

Para observar uma saída do processo, use o Processo classe e assinar o Saiu evento.

Editar: Removido Comentário da Interop.

Editar (em resposta a comentários):

Na maioria das vezes, o que é feito em uma situação como essa é transmitir alguns dados de volta, para que você possa parar de bloquear no Console.In.Read Chamada de método.

Então, suponha que você defina uma bandeira, IsDone, para verdade quando você descobrir que o processo pai está feito. Agora, como o processo que você está esperando mais não envia nada, você ainda precisa receber algo sobre entrada padrão ou bloquear para sempre. Portanto, no seu código de manipulador/temporizador de eventos, escreva algo para a entrada padrão do seu próprio processo (pode até ser um valor especial para sinalizar que você deseja, se desejar). Isso vai passar por você além do Console.In.Read método. Uma vez fora, verifique se o IsDone Flag está definido - se for, pare de processar e retorne do seu método principal - não System.Environment.Exit requeridos.

Eu resolveria isso adicionando um thread ao seu aplicativo de console que basicamente monitora a presença do aplicativo de chamada na tabela de processos. Tudo isso está do topo da minha cabeça, mas aqui está alguns pseudocódicos:

using System.Thread;
using System.Diagnostics;

main(){
     Thread monitoringThread = new Thread(new ThreadStart(monitor));
     monitoringThread.Name = "Monitor";
     monitoringThread.Start();
}

e no monitor de funções:

void monitor(){
     Process[] theCallers = Process.GetProcessesByName("CallingProcessName");
     theCallers[0].WaitForExit();
     Process.GetCurrentProcess().Kill();
}

Supondo que você tenha apenas um processo de chamada na lista de processos, assim que esse processo terminar, este também. Você também pode colocar um código de limpeza muito melhor para o seu próprio processo, em vez de tê -lo acabado de terminar abruptamente.

Eu gosto da ideia de MMR de assistir ao processo pai da criança. Se você tiver uma maneira de passar o PID dos pais na linha de comando, isso seria ainda melhor. De dentro do processo pai, você pode obter o PID:

System.Diagnostics.Process.GetCurrentProcess().Id
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top