C# DotNet 3.x 多个 BackgroudServices 不一致 运行
C# DotNet 3.x Multiple BackgroudServices do not run consistently
我在 DotNet 3.1 中有一个 SignalR 应用程序,有点像大型聊天应用程序,我正在尝试添加两个 BackgroundServices。
后台服务设置为 运行,只要 ASP.NET 应用 运行 就可以了。
第一个 BackgroundService 有一个非常快的主循环(50 毫秒)并且看起来运行良好。
第二个 BackgroundService 有一个更长的主循环(1000 毫秒)并且似乎随机开始,随机停止执行,然后重新开始执行......随机。这几乎就像第二个机器人要睡了很长一段时间(30 到 90 秒),然后再次醒来并保留了对象状态。
两个 BackgroundServices 具有相同的基本代码,但延迟不同。
是否可以有多个独立的、无休止的后台服务?如果是这样,那我做错了什么?
我有这样注册的服务...
_services.AddSimpleInjector(_simpleInjectorContainer, options =>
{
options.AddHostedService<SecondaryBackgroundService>();
options.AddHostedService<PrimaryBackgroundService>();
// AddAspNetCore() wraps web requests in a Simple Injector scope.
options.AddAspNetCore()
// Ensure activation of a specific framework type to be created by
// Simple Injector instead of the built-in configuration system.
.AddControllerActivation()
.AddViewComponentActivation()
.AddPageModelActivation()
.AddTagHelperActivation();
});
我有两个 classes (PrimaryBackgroundService/SecondaryBackgroundService) 有这个 ...
public class SecondaryBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await Task.Factory.StartNew(async () =>
{
// loop until a cancalation is requested
while (!cancellationToken.IsCancellationRequested)
{
//await Task.Delay(TimeSpan.FromMilliseconds(50), cancellationToken);
await Task.Delay(TimeSpan.FromMilliseconds(1000), cancellationToken);
try
{
await _doWorkDelegate();
}
catch (Exception ex)
{
}
}
}, cancellationToken);
}
}
我应该设置一个 BackgroundService 来分离两个不同的任务吗?在他们自己的线程中?我应该改用 IHostedService 吗?
我需要确保每秒第二个 BackgroundService 运行s。此外,我需要确保第二个 BackgroundService 永远不会影响更快的 运行ning 主 BackgroundService。
更新:
我按照建议更改了代码以使用定时器,但现在我正在努力从定时器事件调用异步任务。
这是我用不同的选项创建的 class,有效和无效。
// used this as the base: https://github.com/aspnet/Hosting/blob/master/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs
public abstract class RecurringBackgroundService : IHostedService, IDisposable
{
private Timer _timer;
protected int TimerIntervalInMilliseconds { get; set; } = 250;
// OPTION 1. This causes strange behavior; random starts and stops
/*
protected abstract Task DoRecurringWork();
private async void OnTimerCallback(object notUsedTimerState) // use "async void" for event handlers
{
try
{
await DoRecurringWork();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 2. This causes strange behavior; random starts and stops
/*
protected abstract Task DoRecurringWork();
private void OnTimerCallback(object notUsedTimerState)
{
try
{
var tf = new TaskFactory(System.Threading.CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
tf.StartNew(async () =>
{
await DoRecurringWork();
})
.Unwrap()
.GetAwaiter()
.GetResult();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 3. This works but requires the drived to have "async void"
/*
protected abstract void DoRecurringWork();
private void OnTimerCallback(object notUsedTimerState)
{
try
{
DoRecurringWork(); // use "async void" in the derived class
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 4. This works just like OPTION 3 and allows the drived class to use a Task
protected abstract Task DoRecurringWork();
protected async void DoRecurringWorkInternal() // use "async void"
{
await DoRecurringWork();
}
private void OnTimerCallback(object notUsedTimerState)
{
try
{
DoRecurringWork();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
public virtual Task StartAsync(CancellationToken cancellationToken)
{
//
// do a single call timer pulse
_timer = new Timer(OnTimerCallback, null, this.TimerIntervalInMilliseconds, Timeout.Infinite);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
try { _timer.Change(Timeout.Infinite, 0); } catch {; }
return Task.CompletedTask;
}
public void Dispose()
{
try { _timer.Change(Timeout.Infinite, 0); } catch {; }
try { _timer.Dispose(); } catch {; }
}
}
选项 3 and/or 选项 4 正确吗?
我已确认选项 3 和选项 4 重叠。我怎样才能阻止它们重叠? (更新:使用选项 1)
更新
看来选项 1 还是正确的。
Stephen Cleary 是正确的。在深入研究代码之后,我确实找到了一个在 _doWorkDelegate()
方法下停止执行的任务。随机启动和停止是由失败的 HTTP 调用引起的。一旦我解决了这个问题(一劳永逸),选项 1 开始按预期工作。
我建议编写两个定时后台任务,如文档中所示
Timed background tasks documentation
那么他们就是独立和孤立的。
public class PrimaryBackgroundService : IHostedService, IDisposable
{
private readonly ILogger<PrimaryBackgroundService> _logger;
private Timer _timer;
public PrimaryBackgroundService(ILogger<PrimaryBackgroundService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PrimaryBackgroundService StartAsync");
TimeSpan waitTillStart = TimeSpan.Zero;
TimeSpan intervalBetweenWork = TimeSpan.FromMilliseconds(50);
_timer = new Timer(DoWork, null, waitTillStart, intervalBetweenWork);
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("PrimaryBackgroundService DoWork");
// ... do work
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PrimaryBackgroundService is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
使用类似的代码创建 SecondaryBackgroundService
并像以前一样注册它们
options.AddHostedService<SecondaryBackgroundService>();
options.AddHostedService<PrimaryBackgroundService>();
请注意,如果您想使用任何依赖注入,则必须将 IServiceScopeFactory
注入后台服务构造函数并调用 scopeFactory.CreateScope()
我在 DotNet 3.1 中有一个 SignalR 应用程序,有点像大型聊天应用程序,我正在尝试添加两个 BackgroundServices。
后台服务设置为 运行,只要 ASP.NET 应用 运行 就可以了。
第一个 BackgroundService 有一个非常快的主循环(50 毫秒)并且看起来运行良好。
第二个 BackgroundService 有一个更长的主循环(1000 毫秒)并且似乎随机开始,随机停止执行,然后重新开始执行......随机。这几乎就像第二个机器人要睡了很长一段时间(30 到 90 秒),然后再次醒来并保留了对象状态。
两个 BackgroundServices 具有相同的基本代码,但延迟不同。
是否可以有多个独立的、无休止的后台服务?如果是这样,那我做错了什么?
我有这样注册的服务...
_services.AddSimpleInjector(_simpleInjectorContainer, options =>
{
options.AddHostedService<SecondaryBackgroundService>();
options.AddHostedService<PrimaryBackgroundService>();
// AddAspNetCore() wraps web requests in a Simple Injector scope.
options.AddAspNetCore()
// Ensure activation of a specific framework type to be created by
// Simple Injector instead of the built-in configuration system.
.AddControllerActivation()
.AddViewComponentActivation()
.AddPageModelActivation()
.AddTagHelperActivation();
});
我有两个 classes (PrimaryBackgroundService/SecondaryBackgroundService) 有这个 ...
public class SecondaryBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await Task.Factory.StartNew(async () =>
{
// loop until a cancalation is requested
while (!cancellationToken.IsCancellationRequested)
{
//await Task.Delay(TimeSpan.FromMilliseconds(50), cancellationToken);
await Task.Delay(TimeSpan.FromMilliseconds(1000), cancellationToken);
try
{
await _doWorkDelegate();
}
catch (Exception ex)
{
}
}
}, cancellationToken);
}
}
我应该设置一个 BackgroundService 来分离两个不同的任务吗?在他们自己的线程中?我应该改用 IHostedService 吗?
我需要确保每秒第二个 BackgroundService 运行s。此外,我需要确保第二个 BackgroundService 永远不会影响更快的 运行ning 主 BackgroundService。
更新:
我按照建议更改了代码以使用定时器,但现在我正在努力从定时器事件调用异步任务。
这是我用不同的选项创建的 class,有效和无效。
// used this as the base: https://github.com/aspnet/Hosting/blob/master/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs
public abstract class RecurringBackgroundService : IHostedService, IDisposable
{
private Timer _timer;
protected int TimerIntervalInMilliseconds { get; set; } = 250;
// OPTION 1. This causes strange behavior; random starts and stops
/*
protected abstract Task DoRecurringWork();
private async void OnTimerCallback(object notUsedTimerState) // use "async void" for event handlers
{
try
{
await DoRecurringWork();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 2. This causes strange behavior; random starts and stops
/*
protected abstract Task DoRecurringWork();
private void OnTimerCallback(object notUsedTimerState)
{
try
{
var tf = new TaskFactory(System.Threading.CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
tf.StartNew(async () =>
{
await DoRecurringWork();
})
.Unwrap()
.GetAwaiter()
.GetResult();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 3. This works but requires the drived to have "async void"
/*
protected abstract void DoRecurringWork();
private void OnTimerCallback(object notUsedTimerState)
{
try
{
DoRecurringWork(); // use "async void" in the derived class
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
*/
// OPTION 4. This works just like OPTION 3 and allows the drived class to use a Task
protected abstract Task DoRecurringWork();
protected async void DoRecurringWorkInternal() // use "async void"
{
await DoRecurringWork();
}
private void OnTimerCallback(object notUsedTimerState)
{
try
{
DoRecurringWork();
}
finally
{
// do a single call timer pulse
_timer.Change(this.TimerIntervalInMilliseconds, Timeout.Infinite);
}
}
public virtual Task StartAsync(CancellationToken cancellationToken)
{
//
// do a single call timer pulse
_timer = new Timer(OnTimerCallback, null, this.TimerIntervalInMilliseconds, Timeout.Infinite);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
try { _timer.Change(Timeout.Infinite, 0); } catch {; }
return Task.CompletedTask;
}
public void Dispose()
{
try { _timer.Change(Timeout.Infinite, 0); } catch {; }
try { _timer.Dispose(); } catch {; }
}
}
选项 3 and/or 选项 4 正确吗?
我已确认选项 3 和选项 4 重叠。我怎样才能阻止它们重叠? (更新:使用选项 1)
更新
看来选项 1 还是正确的。
Stephen Cleary 是正确的。在深入研究代码之后,我确实找到了一个在 _doWorkDelegate()
方法下停止执行的任务。随机启动和停止是由失败的 HTTP 调用引起的。一旦我解决了这个问题(一劳永逸),选项 1 开始按预期工作。
我建议编写两个定时后台任务,如文档中所示
Timed background tasks documentation
那么他们就是独立和孤立的。
public class PrimaryBackgroundService : IHostedService, IDisposable
{
private readonly ILogger<PrimaryBackgroundService> _logger;
private Timer _timer;
public PrimaryBackgroundService(ILogger<PrimaryBackgroundService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PrimaryBackgroundService StartAsync");
TimeSpan waitTillStart = TimeSpan.Zero;
TimeSpan intervalBetweenWork = TimeSpan.FromMilliseconds(50);
_timer = new Timer(DoWork, null, waitTillStart, intervalBetweenWork);
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("PrimaryBackgroundService DoWork");
// ... do work
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("PrimaryBackgroundService is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
使用类似的代码创建 SecondaryBackgroundService
并像以前一样注册它们
options.AddHostedService<SecondaryBackgroundService>();
options.AddHostedService<PrimaryBackgroundService>();
请注意,如果您想使用任何依赖注入,则必须将 IServiceScopeFactory
注入后台服务构造函数并调用 scopeFactory.CreateScope()