防止 .Net Core 5 控制台应用程序在 Linux 作为服务上运行时结束的推荐方法
Posted
技术标签:
【中文标题】防止 .Net Core 5 控制台应用程序在 Linux 作为服务上运行时结束的推荐方法【英文标题】:Recommended way to prevent a .Net Core 5 console app from ending when run on Linux as a service 【发布时间】:2021-06-05 14:08:48 【问题描述】:我有一个在 Linux (Debian 10) 上运行的 .Net Core 5 控制台应用程序。基本结构是这样的:
class Program
static async Task Main(string[] args)
await SetupStuffAsync();
MonitorGpioservice.Run();
RunAScheduledServiceOnATimer();
Console.ReadLine();
基本上,它在 Orange Pi Zero(类似于 Raspberry Pi)上运行,等待 GPIO 引脚上的信号。当该信号到达时,它会读取串行端口几毫秒,将数据写入 MariaDB 数据库(使用 EF Core),然后将数据发布到 Web API。它还每 5 分钟运行一次计划维护代码(使用 System.Timers.Timer())。
此应用程序在无人看管的情况下运行 - 甚至没有屏幕 - 并且必须始终运行,从 Orange Pi 启动到关闭。
当我在开发过程中手动运行应用程序时,Console.ReadLine() 可以很好地阻止应用程序结束。 但是现在我需要应用程序在 Debian 启动时自动运行,所以我做了以下操作:
sudo nano /etc/systemd/system/orangePi.service
[Unit]
Description=orangePi.service
[Service]
Type=simple
ExecStart=/root/dotnet/dotnet sr/local/bin/orangePiService/orangePiService.dll
Restart=on-failure
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
这很好用 - 应用程序会在启动时自动启动,但出现了问题。 Console.ReadLine() 被完全忽略。它从字面上执行一切,然后结束。我想这是有道理的,因为在这种情况下它没有在控制台中运行。
我知道我可以,例如,在末尾放置一个无限循环以防止它结束:
class Program
static async Task Main(string[] args)
await SetupStuffAsync();
MonitorGpioService.Run();
RunAScheduledServiceOnATimer();
while (0 == 0) ;
这行得通,但我不喜欢它。它不仅不漂亮,而且我想它会占用大量 CPU 来运行该循环。
我可以这样做:
class Program
static async Task Main(string[] args)
await SetupStuffAsync();
MonitorGpioService.Run();
RunAScheduledServiceOnATimer();
while (0 == 0)
Thread.Sleep(int.MaxValue);
;
我想这对 CPU 的负担会更少,但我认为它阻塞了这个线程。那是问题吗?我知道这取决于其余代码的外观,但是要在此处发布相当多的代码。我可以说的是,大部分操作都发生在 MonitorGpioService.Run() 中,我在下面以极其简化的格式发布:
using System.Device.Gpio;
public static class MonitorGpioService()
static GpioController _controller;
public static void Run()
_controller = new GpioController();
_controller.RegisterCallbackForPinValueChangedEvent((int)Settings.GpioPin.Read, PinEventTypes.Rising, onSignalPinValueChangedEvent);
private static void onSignalPinValueChangedEvent(object sender, PinValueChangedEventArgs args)
string data = ReadSerialPortFor40ms();
using (var context = new eballContext())
await _dbContext.Readings.AddRangeAsync(readings);
await _dbContext.SaveChangesAsync();
我尽可能使用等待,我认为这不会受到主线程被阻塞的影响。我不确定当 GPIO 引脚状态发生变化时事件处理程序的触发。从我的测试来看,它似乎并没有受到阻塞主线程的影响,但我不能确定......
总之(我为这篇文章的篇幅道歉),我试图找出在 Linux 作为服务运行时防止 .Net Core 控制台应用程序退出的最佳方法。最好的情况是,我的意思是消耗尽可能少的 CPU,或者尽可能少地阻塞线程(假设这甚至是一个开始的问题,考虑到我的大部分代码都运行在 Timers 或使用等待)。
【问题讨论】:
这能回答你的问题吗? C# console program wait forever for event 你可以看看worker service template 【参考方案1】:好吧,while 循环是一个美丽的东西,但它是 CPU 的敌人。 而不是 while,我经常使用 ManualResetEvent 类来防止关闭控制台应用程序。 通常,当我在 Docker 上的 Linux 容器中使用此代码块时,它会起作用。 我没有实际的代码块,我会记住它;
public static ManualResetEvent _Shutdown = new ManualResetEvent(false);
static void Main(string[] args)
//Lots of stuff.
_Shutdown.WaitOne();
基本上,关闭信号永远不会出现,控制台应用程序永远不会关闭。当然,您可以根据自己的需要开发更有针对性的代码。这可以防止控制台应用程序在所有这些东西正常工作时关闭。你可以试一试。您还可以在 SO 中找到许多不同的方法。
【讨论】:
【参考方案2】:来自C# console program wait forever for event的答案
在异步主方法的情况下,也可以使用
await Task.Delay(-1);
Task.Delay()
本身通常更优雅,它允许您传递取消令牌,在需要时启用优雅关闭。
Thread.Sleep()
也应该可以工作,但不能取消。您可以使用 Timeout.Infinite
来挂起而不浪费任何循环,而不是使用 while 循环
【讨论】:
以上是关于防止 .Net Core 5 控制台应用程序在 Linux 作为服务上运行时结束的推荐方法的主要内容,如果未能解决你的问题,请参考以下文章
在 .NET 4.5.2 控制台应用程序中使用 .NET Core 库
如何强制更新/防止在 Linux 上运行并由 nginx 反向代理服务器托管的 .NET Core Web 应用程序的 HTML 缓存
.Net or .Net Core依赖注入实现Microsoft.Extensions.DependencyInjection
.Net Core 5 控制台应用程序,在 Program.Main 中完成时不会为环境加载应用程序设置
防止 AddRedirectToWwwPermanent() 在 ASP.NET Core 2.1 中将“www”添加到 *.azurewebsites.net 的前面