Каков правильный способ для программы завершить свой собственный процесс (Windows)

StackOverflow https://stackoverflow.com/questions/1200507

Вопрос

C # .NET 3.5

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

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

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

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

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

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

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

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

Редактировать:

Код, который ожидает ввода из 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().

Это было полезно?

Решение

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

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

Другие советы

Чтобы следить за завершением процесса, используйте Процесс класс и подпишитесь на Вышедший событие.

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

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

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

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

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

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

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

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

System.Diagnostics.Process.GetCurrentProcess().Id
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top