dotnet core, как изящно завершить дочерний процесс

У меня есть консольное приложение dotnet core 2.2.
Я разместил его как службу Windows.
Служба запускает другой веб-API ядра dotnet.
Проблема в том, как корректно завершить процесс WebAPI, когда служба остановлена. ?
Примечание. Я не хочу использовать метод Kill().

Образец кода:

public class MyService : IHostedService, IDisposable
{
    private Timer _timer;
    static Process webAPI;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(
            (e) => StartChildProcess(),
            null,
            TimeSpan.Zero,
            TimeSpan.FromMinutes(1));

        return Task.CompletedTask;
    }

    public void StartChildProcess()
    {
        try
        {
            webAPI = new Process();
            webAPI.StartInfo.UseShellExecute = false;
            webAPI.StartInfo.FileName = @"C:\Project\bin\Debug\netcoreapp2.2\publish\WebAPI.exe";
            webAPI.Start();
        }
        catch (Exception e)
        {
            // Handle exception
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // TODO: Add code to stop child process safely

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

person RPrashant    schedule 16.05.2019    source источник


Ответы (3)


Технически вы можете просто вызвать Process.Kill(), чтобы немедленно остановить процесс. Однако в большинстве случаев это не лучший способ просто потому, что WebAPI может быть в середине важных операций, и вы не можете точно сказать, когда эти действия могут происходить, а Process.Kill() на самом деле не считается «изящным».

Что было бы наиболее благоразумно сделать, так это сообщить процессу, что вы хотели бы, чтобы он закрылся при первой же возможности, а затем позволить WebAPI очистить его до того, как он завершит работу сам. Если вы проектируете WebAPI, это даже лучше, потому что таким образом вы можете решить, как это сделать. Звонить Kill() только тогда, когда это абсолютно необходимо.

Вы можете сделать это несколькими способами, конечно. На ум приходят сокеты, которые периодически проверяются и отправляют ввод CTRL+C в WebAPI.


    public Task StopAsync(CancellationToken cancellationToken)
    {
        // send request to shut down

        // wait for process to exit and free its resources
        process.WaitForExit();
        process.Close();
        process.Dispose();



        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

Конечно, если это асинхронный, то не имеет смысла ждать, пока он завершится внутри метода, поэтому вы просто подождите или проверите, вышел ли он за пределами этого метода.

person ElectrumHafnium    schedule 16.05.2019
comment
Спасибо за помощь. не могли бы вы предоставить пример кода для отправки запроса на закрытие для webapi - person RPrashant; 16.05.2019
comment
Что ж, если вы пытаетесь использовать метод CTRL+C, я бы предложил эту суть в качестве общего руководства. . Оттуда у вас есть выбор: очистить процесс и завершить работу из метода обработчика прерывания (называемого ChildConsoleCtrlCheck), вернув false, или вы можете вернуть true и установить флаг shouldFinish для завершения вне обработчика. Обычно в C (никогда не пробовал это в C#) вы хотите сделать второй вариант, так как вы не остаетесь в обработчике надолго, и программа заканчивает то, что она делала, когда она была прервана. - person ElectrumHafnium; 17.05.2019
comment
Если вы хотите сделать версию сокета (которая мне кажется более высокоуровневой и созвучной с C#), есть несколько примеров связи с сокетом в C#. Вот простой, но хороший - person ElectrumHafnium; 17.05.2019
comment
Опять же, оба этих метода требуют некоторого доступа к реализации WebAPI. Насколько мне известно, не существует изящного метода для уничтожения процесса, который понятия не имеет, что его можно убить. - person ElectrumHafnium; 17.05.2019
comment
по вашему предложению я использовал это. Это работает в моем приложении. Спасибо! - person RPrashant; 25.06.2019

На github было много тем по этой проблеме, рассмотрим сообщение 7426.

Решение находится здесь: StartAndStopDotNetCoreApp, пример кода программы.cs является:

using System;
using System.IO;
using System.Diagnostics;

namespace StartAndStopDotNetCoreApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string projectPath = @"C:\source\repos\StartAndStopDotNetCoreApp\WebApplication";
            string outputPath = @"C:\Temp\WebApplication";

            Console.WriteLine("Starting the app...");
            var process = new Process();
            process.StartInfo.WorkingDirectory = projectPath;
            process.StartInfo.FileName = "dotnet";
            process.StartInfo.Arguments = $"publish -o {outputPath}";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.Start();
            process.WaitForExit();
            process.Close();
            process.Dispose();

            process = new Process();
            process.StartInfo.WorkingDirectory = outputPath;
            process.StartInfo.FileName = "dotnet";
            process.StartInfo.Arguments = $"{projectPath.Split(@"\")[projectPath.Split(@"\").Length - 1]}.dll";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = false;
            process.StartInfo.RedirectStandardOutput = true;

            process.Start();

            Console.WriteLine("Press anything to stop...");
            Console.Read();

            process.Kill();
        }
    }
}

Если это не то, что вы ищете, поищите в упомянутой ветке другие способы, там их много.

person Barr J    schedule 16.05.2019
comment
Спасибо за помощь. Я не хочу использовать метод Kill(). Есть ли другой способ? - person RPrashant; 16.05.2019

Возможно, можно использовать метод CloseMainWindow класса System.Diagnostics.Process? Я наткнулся на него, когда искал способ отправить сообщение WM_CLOSE другому процессу из C#.

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

CloseMainWindow не работает, если главное окно "заблокировано", например, открытым модальным окном или диалогом.

Вы можете изучить стратегию отправки сообщения WM_CLOSE или WM_DESTROY в процесс вашего приложения в явном виде. Но я не могу гарантировать, будет ли это работать так, как вы хотите/ожидаете. Я очень давно не работал с такой функциональностью Windows Messages.

person Bart Hofland    schedule 16.05.2019