dotnet core 如何优雅地关闭子进程

dotnet core how to shutdown child process gracefully

我有一个 dotnet core 2.2 控制台应用程序。
我将其托管为 windows 服务。
服务启动另一个 dotnet 核心 WebAPI。
问题是,如何在服务停止时优雅地关闭 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();
    }
}

github 上有很多关于此问题的话题,请考虑 post 7426

解决方法在这里:StartAndStopDotNetCoreApp,program.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();
        }
    }
}

如果这不是您要查找的内容,请搜索提到的线程以获取更多方法,它提供了很多方法。

从技术上讲,您只需调用 Process.Kill() 即可立即关闭进程。但是,很多时候这并不是正确的方法,因为 WebAPI 可能正处于重要操作的中间,您无法真正判断这些操作何时可能发生并且 Process.Kill() 并未真正考虑 "graceful".

最谨慎的做法是告诉进程您喜欢让它在方便的时候尽早关闭,然后让 WebAPI 进行清理在它自己退出之前。如果您正在设计更好的 WebAPI,因为那样您可以决定如何执行此操作。仅在绝对必要时才调用 Kill()

当然,您可以通过多种方式做到这一点。我想到的一些是定期检查并向 WebAPI 发送 CTRL+C 输入的套接字。


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

当然,如果这是 Async,那么等待它在方法内部退出是没有意义的,因此您只需等待或检查它是否已在该方法外部退出。

也许可以使用System.Diagnostics.Processclass的CloseMainWindow方法?我在寻找一种从 C# 向另一个进程发送 WM_CLOSE 消息的方法时偶然发现了它。

编辑:

如果主要 window 是 "blocked",

CloseMainWindow 似乎不起作用,例如通过打开模式 window 或对话框。

您可能会研究一种向您的应用进程显式发送 WM_CLOSE 或 WM_DESTROY 消息的策略。但我不能保证它是否会像你一样工作want/expect。我已经很长时间没有使用那种 Windows 消息功能了。