Le mieux que j'ai pu trouver est basé sur la réponse à deux autres questions :tuer gracieusement un démon .NET Core fonctionnant sous Linux et est-il possible d'attendre un événement au lieu d'une autre méthode asynchrone ?
using System;
using System.Runtime.Loader;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Program
{
private static TaskCompletionSource<object> taskToWait;
public static void Main(string[] args)
{
taskToWait = new TaskCompletionSource<object>();
AssemblyLoadContext.Default.Unloading += SigTermEventHandler;
Console.CancelKeyPress += new ConsoleCancelEventHandler(CancelHandler);
//eventSource.Subscribe(eventSink) or something...
taskToWait.Task.Wait();
AssemblyLoadContext.Default.Unloading -= SigTermEventHandler;
Console.CancelKeyPress -= new ConsoleCancelEventHandler(CancelHandler);
}
private static void SigTermEventHandler(AssemblyLoadContext obj)
{
System.Console.WriteLine("Unloading...");
taskToWait.TrySetResult(null);
}
private static void CancelHandler(object sender, ConsoleCancelEventArgs e)
{
System.Console.WriteLine("Exiting...");
taskToWait.TrySetResult(null);
}
}
}
J'ai joué avec une idée similaire à la façon dont l'hôte Web .net core attend l'arrêt dans les applications console. Je l'examinais sur GitHub et j'ai pu extraire l'essentiel de la façon dont ils ont exécuté le Run
https://github.com/aspnet/Hosting/blob/15008b0b7fcb54235a9de3ab844c066aaf42ea44/src/Microsoft.AspNetCore.Hosting/WebHostExtensions.cs#L86
public static class ConsoleHost {
/// <summary>
/// Block the calling thread until shutdown is triggered via Ctrl+C or SIGTERM.
/// </summary>
public static void WaitForShutdown() {
WaitForShutdownAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Runs an application and block the calling thread until host shutdown.
/// </summary>
/// <param name="host">The <see cref="IWebHost"/> to run.</param>
public static void Wait() {
WaitAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Runs an application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="host">The <see cref="IConsoleHost"/> to run.</param>
/// <param name="token">The token to trigger shutdown.</param>
public static async Task WaitAsync(CancellationToken token = default(CancellationToken)) {
//Wait for the token shutdown if it can be cancelled
if (token.CanBeCanceled) {
await WaitAsync(token, shutdownMessage: null);
return;
}
//If token cannot be cancelled, attach Ctrl+C and SIGTERN shutdown
var done = new ManualResetEventSlim(false);
using (var cts = new CancellationTokenSource()) {
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: "Application is shutting down...");
await WaitAsync(cts.Token, "Application running. Press Ctrl+C to shut down.");
done.Set();
}
}
/// <summary>
/// Returns a Task that completes when shutdown is triggered via the given token, Ctrl+C or SIGTERM.
/// </summary>
/// <param name="token">The token to trigger shutdown.</param>
public static async Task WaitForShutdownAsync(CancellationToken token = default (CancellationToken)) {
var done = new ManualResetEventSlim(false);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token)) {
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: string.Empty);
await WaitForTokenShutdownAsync(cts.Token);
done.Set();
}
}
private static async Task WaitAsync(CancellationToken token, string shutdownMessage) {
if (!string.IsNullOrEmpty(shutdownMessage)) {
Console.WriteLine(shutdownMessage);
}
await WaitForTokenShutdownAsync(token);
}
private static void AttachCtrlcSigtermShutdown(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage) {
Action ShutDown = () => {
if (!cts.IsCancellationRequested) {
if (!string.IsNullOrWhiteSpace(shutdownMessage)) {
Console.WriteLine(shutdownMessage);
}
try {
cts.Cancel();
} catch (ObjectDisposedException) { }
}
//Wait on the given reset event
resetEvent.Wait();
};
AppDomain.CurrentDomain.ProcessExit += delegate { ShutDown(); };
Console.CancelKeyPress += (sender, eventArgs) => {
ShutDown();
//Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
}
private static async Task WaitForTokenShutdownAsync(CancellationToken token) {
var waitForStop = new TaskCompletionSource<object>();
token.Register(obj => {
var tcs = (TaskCompletionSource<object>)obj;
tcs.TrySetResult(null);
}, waitForStop);
await waitForStop.Task;
}
}
J'ai essayé d'adapter quelque chose comme un IConsoleHost
mais je me suis vite rendu compte que j'étais trop ingénieur. Extrait les parties principales dans quelque chose comme await ConsoleUtil.WaitForShutdownAsync();
qui fonctionnait comme Console.ReadLine
Cela a ensuite permis à l'utilitaire d'être utilisé comme ceci
public class Program {
public static async Task Main(string[] args) {
//relevant code goes here
//...
//wait for application shutdown
await ConsoleUtil.WaitForShutdownAsync();
}
}
à partir de là, créer un systemd comme dans le lien suivant devrait vous permettre d'aller jusqu'au bout
Écrire un démon Linux en C#