我正在测试一个.net核心6多线程服务工作者应用程序,它在sql server上处理成批的数据行和表。
我只使用serilog记录文件。
当我在调试模式下运行它时,会打开一个控制台窗口,但它是空的,仍然是空的。我使用计数器进行测试,这样在循环了这么多循环之后,主工作器中的ExecuteAsync()函数就会退出。
我注意到主工作人员返回后,我的program.cs main方法中的最后一个块没有中断(我有一个断点),程序在关闭控制台之前不会“返回”到visual,甚至没有控制台消息要求我按键。即使关闭控制台,最后{Log.CloseAndFlush}块也不会中断。之后,我发现我的调试输出打印了许多类似于
“线程0x7590已退出代码0 (0x0)。
线程0xdcc使用代码0 (0x0)退出。
线程0x6c68已退出代码0 (0x0)。
线程0x690c与代码0 (0x0)一起退出。
线程0x3ec0与代码0 (0x0)一起退出。
线程0x7f08已退出代码0(0x0)。“
关闭控制台窗口后,我得到以下输出
“程序'18244 pulse.deletemember.service.exe:程序跟踪‘与代码0 (0x0)一起退出。
程序“18244 pulse.deletemember.service.exe”的代码为3221225786 (0xc000013a)。
我已经包括了主要代码。有人能告诉我为什么会发生这种事吗?在类似的帖子中,有人认为这可能是由未处理的东西造成的。我通常使用BackgroundWorker,因为我完全了解他们的行为和处理他们等,这是等待任务的东西对我来说是新的。
program.cs
using Serilog;
namespace pulse.deletemember.service;
public class Program
{
// With help from https://www.youtube.com/watch?v=_iryZxv8Rxw&t=1055s
public static void Main(string[] args)
{
try
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
// Create temp logger before dependency injection takes over
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(MyConfiguration.ConfigurationSettings)
.CreateLogger();
Log.Logger.Information("Application starting up");
// Start the application and wait for it to complete
CreateHostBuilder(args).Build().Run();
}
catch (Exception err)
{
Log.Fatal(err, "The application failed to start correctly.");
}
finally
{
Log.CloseAndFlush();
}
}
/// <summary>
/// This sets up app
/// </summary>
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
.UseSerilog()
.ConfigureServices(ConfigureDelegate)
;
}
/// <summary>
/// Example of Dependency Injection
/// Injecting singleton instance of MyConfiguration.
/// Tested that config is re-read if changed
/// </summary>
/// <param name="hostContext"></param>
/// <param name="services"></param>
private static void ConfigureDelegate(HostBuilderContext hostContext, IServiceCollection services)
{
// Injects Worker thread. This adds a singleton that lasts length of the application
services.AddHostedService<Worker>();
services.AddSingleton<IMyConfiguration,MyConfiguration>();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IMemoryQueryClass, MemoryQueryClass>();
}
}worker.cs
using System.Data.SqlClient;
namespace pulse.deletemember.service
{
internal class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IMyConfiguration _configuration;
private readonly IRepository _repository;
private readonly IMemoryQueryClass _memoryQueryClass;
private readonly List<Task> _taskQueue = new();
private long _counter;
public Worker(ILogger<Worker> logger, IMyConfiguration configuration, IRepository repository,
IMemoryQueryClass memoryQueryClass)
{
_logger = logger;
_configuration = configuration;
_repository = repository;
_memoryQueryClass = memoryQueryClass;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Initialise the delay from config file
var delay = _configuration.PollDelaySecs;
_logger.LogInformation("Worker running at: {time} ({e} utc)", DateTime.UtcNow.ToString("g"),
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT"));
while (!stoppingToken.IsCancellationRequested)
{
// are we finished yet?
if (_configuration.MaxCounter >= 0 && _counter >= _configuration.MaxCounter) break;
_counter += _configuration.BatchSize;
try
{
var memoryUsed = _memoryQueryClass.PrivateMemorySize;
_logger.LogInformation("Loop starting at {time} utc. Private memory = {memoryUsed:N1} Mb",
DateTime.UtcNow.ToString("g"),memoryUsed);
await Task.Delay(delay * 1000, stoppingToken);
/* https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/asynchronous-programming */
_repository.ResetMutexes();
stoppingToken.ThrowIfCancellationRequested();
// Delete old records in membership deletions log table
_repository.TruncateDeletionsLogTable();
// Pull in a bunch of member rows for deletion
using var members = _repository.ReadBatch();
if (members.Count == 0)
{
// zero records read, so set delay to 'normal' interval and start loop again
delay = _configuration.PollDelaySecs;
continue;
}
// Set delay to zero while more than zero records read from deletions table
delay = 0;
var taskQueueQuery =
from memberRow in members
select _repository.MainDeleteProcessAsync(memberRow, stoppingToken);
// Convert query to enumerable list
_taskQueue.AddRange(taskQueueQuery.ToList());
// While any task is available in the list, keep waiting.
// Once queue is empty then start overall loop again
while (_taskQueue.Any())
{
var finishedTask = await Task.WhenAny(_taskQueue);
// remove task from queue so taskQueue knows when to exit loop
_taskQueue.Remove(finishedTask);
stoppingToken.ThrowIfCancellationRequested();
}
}
catch (SqlException err)
{
// As errors are handled in their own threads, this handler will only
// catch when reading in batch.
_logger.LogError(err, "Sql Exception in worker");
if (err.Number == 18487 || err.Number == 18488)
_logger.LogError("err.Number == 18487 || err.Number == 18488, password expired or needs to be reset");
// Set delay for a min to allow for reboots or whatever
delay = 60;
}
catch (OperationCanceledException err)
{
_logger.LogError(err, "Stoppingtoken operation was cancelled");
break;
}
catch (Exception err)
{
_logger.LogError(err, "General error caught in worker loop. Ignoring");
}
} // while
if (stoppingToken.IsCancellationRequested)
_logger.LogInformation("stoppingToken.Cancelled set. Exiting application");
_logger.LogInformation("Service exited. Counter = {counter}",_counter);
}// function
}
}发布于 2022-05-13 07:03:01
我试着用谷歌的几个查询找到答案,幸运的是我找到了答案。
首先,我发现服务的行为不像应用程序,即使在工作线程退出之后,它们也会继续运行。Tbh,为什么这是默认行为是一个谜,会有兴趣知道为什么。
How do I (gracefully) shut down a worker service from within itself?
在那里,我想知道如何创建一个可以在我的应用程序中使用的IHostApplicationLifetime实例。就在那时,我发现了一个关于上述链接的后续问题。它的实例已经被支持,只需要添加到工作线程的构造函数参数中。
How to get and inject the IHostApplicationLifetime in my service to the container (Console App)
我尝试添加_hostApplicationLifetime.StopApplication();作为worker循环中的最后一行,它成功了。在program.cs恢复中,我在控制台窗口中收到一个通知,日志文件被刷新和关闭。
但我仍然想了解为什么应用程序继续运行,尽管主循环退出,如果有人可以解释请解释。
https://stackoverflow.com/questions/72214184
复制相似问题