Какой правильный способ для программы завершить свой собственный процесс (Windows) - PullRequest
2 голосов
/ 29 июля 2009

C # .NET 3.5

У меня есть консольное приложение, которое вызывается другим приложением на компьютере. Это консольное приложение работает постоянно и прослушивает данные на stdin от «родительского» процесса.

Однако, когда родительский объект остановлен или уничтожен, запущенное им консольное приложение продолжается. При нормальных обстоятельствах он сидит и бездействует в ожидании ввода от стандартного ввода, используя минимальные ресурсы. Однако, как только родитель уходит, это консольное приложение нагружает процессор и истощает ядро, на котором оно работает, почти на 100%. Это продолжается до тех пор, пока я не убью процесс вручную.

В идеале, вызывающий родитель должен убирать за собой, особенно если это происходит при нормальных (не исключительных) условиях «остановки». К сожалению, этот родительский процесс не в моих руках.

Моей первой мыслью было получить вызывающего родителя из консольного приложения и отслеживать его PID. Если родительский процесс исчезнет, ​​я консольное приложение завершит сам. В настоящее время я делаю это:

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);

Это работает только частично - это останавливает всплеск ЦП, но если я посмотрю на свои процессы из замечательного монитора процессов Sysinternals, то увижу, что работает DW20.exe - программа «Отчеты об ошибках приложения Microsoft». И он просто ... сидит там, и консольное приложение остается в памяти.

Что я должен сделать здесь, чтобы правильно завершить процесс, чтобы избежать этого непрерывного скачка ЦП и неизданной памяти? В конце концов это должно быть запущено без вмешательства.

P.S. Я использую приложение командной строки здесь как «долго работающую программу» вместо службы Windows или веб-службы, потому что родительская программа может быть настроена только для запуска приложения командной строки, для которого она передает данные через stdin. (для тех, кому интересно, это ejabberd, использующий внешнюю аутентификацию).

EDIT:

Код, ожидающий ввода от stdin:

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

Я уже упоминал, что когда родительский процесс завершается, консольное приложение сходит с ума на процессоре. Я подключил к нему отладчик из Visual Studio, и он, по сути, все еще находится на этой строке Console.In.Read. Теоретически, когда таймер самоконтроля срабатывает и видит, что родительский элемент отсутствует, он пытается System.Environment.Exit (0), когда другой поток находится в этой строке Read ().

Ответы [ 4 ]

5 голосов
/ 29 июля 2009

Похоже, что ваш процесс входит в жесткий цикл из-за закрытия входного потока консоли при выходе из родительского процесса. Вы проверяете возвращаемое значение из Console.In.Read? Он вернет ноль, когда поток будет закрыт. В этот момент вырвитесь из цикла и позвольте вашему Main() методу выйти самостоятельно.

И если вы используете несколько потоков, сначала их нужно будет завершить, используя Thread.Join или эквивалентный.

3 голосов
/ 29 июля 2009

Для отслеживания выхода из процесса используйте класс Процесс и подпишитесь на событие Exited .

Редактировать: Удален комментарий о взаимодействии.

Редактировать (в ответ на комментарии):

В большинстве случаев в такой ситуации выполняется передача некоторых данных обратно, поэтому вы можете прекратить блокировку при вызове метода Console.In.Read.

Итак, предположим, что вы установили флаг IsDone в значение true, когда обнаружите, что родительский процесс завершен. Теперь, поскольку процесс, который вы ожидаете, больше ничего не отправляет, вам все равно нужно получать что-то на стандартный ввод, или вы заблокируете навсегда. Итак, в вашем коде обработчика событий / таймера напишите что-нибудь на стандартный ввод вашего собственного процесса (это может быть даже специальное значение, чтобы сигнализировать, что вы сделали, если хотите). Это поможет вам преодолеть метод Console.In.Read. После выхода проверьте, установлен ли флаг IsDone - если это так, остановите обработку и вернитесь из основного метода - не требуется System.Environment.Exit.

2 голосов
/ 29 июля 2009

Я бы решил эту проблему, добавив в ваше консольное приложение поток, который в основном отслеживает присутствие вызывающего приложения в таблице процессов. Это все из головы, но вот псевдокод:

using System.Thread;
using System.Diagnostics;

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

и в мониторе функций:

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

При условии, что у вас есть только один вызывающий процесс в списке процессов, как только этот процесс завершится, этот тоже будет. Вы также можете добавить гораздо более приятный код очистки для своего собственного процесса, вместо того, чтобы просто внезапно завершить его.

0 голосов
/ 29 июля 2009

Мне нравится идея ММР наблюдать за родительским процессом от ребенка. Если у вас есть способ передать PID родителя в командной строке, это было бы еще лучше. Внутри родительского процесса вы можете получить PID:

System.Diagnostics.Process.GetCurrentProcess().Id
...