.Net Core 上的计划任务
Scheduled task on .Net Core
我需要将几个计划任务从 widows 服务器的计划程序任务转换为使用 .Net 的独立应用程序。
过去,我在 .Net 框架上使用过 Quartz 4.x,在基于不同调度程序的多个长 运行ning 任务中遇到一些小问题。
现在我正在使用 .Net 5,我想知道是否有一种新的方式来安排任务,比如工作人员服务,或者它仍然更好更灵活地使用 Quartz.Net。
由于我需要 运行 长时间任务,从 30 秒到 2 小时,我需要创建一个定时后台任务,使用 System.Threading.Timer
代码应该如下:
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");
_timer = new Timer(DoSomething, null, TimeSpan.Zero,
TimeSpan.FromHours(24));
return Task.CompletedTask;
}
它应该每 24 小时调用一次 DoSomething。
我的疑惑是:
- 它什么时候开始工作并计算 24 小时,当我第一次 运行
应用程序?
- 怎么说任务必须运行在
一天中的特定时间,例如午夜?
- Worker Service适合管理定时任务吗?
When does it start to work and to count the 24hrs, when I first run the application?
是的。设置断点并启动您的应用程序。你会看到它触发的速度有多快。
Is the Worker Service suitable for managing scheduled tasks?
是的。
How can I say that the task must be run at a specific time in a day, at midnight for example?
让我们看一下这段代码:
public sealed class MyTimedBackgroundService : BackgroundService
{
private static int SecondsUntilMidnight()
{
return (int)(DateTime.Today.AddDays(1.0) - DateTime.Now).TotalSeconds;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var countdown = SecondsUntilMidnight();
while (!stoppingToken.IsCancellationRequested)
{
if (countdown-- <= 0)
{
try
{
await OnTimerFiredAsync(stoppingToken);
}
catch(Exception ex)
{
// TODO: log exception
}
finally
{
countdown = SecondsUntilMidnight();
}
}
await Task.Delay(1000, stoppingToken);
}
}
private async Task OnTimerFiredAsync(CancellationToken stoppingToken)
{
// do your work here
Debug.WriteLine("Simulating heavy I/O bound work");
await Task.Delay(2000, stoppingToken);
}
}
这根本不使用 System.Threading.Timer
,因为您担心计时器由于某些边界而从未真正触发。有些人对此很偏执。我从未遇到过这种事。我经常使用 Timer
进行此类工作。
它将计算到午夜的秒数然后循环直到它到达那里。
这是一个不可重入的定时器,由于处理延迟的业务逻辑,会有轻微的时间滑移。
这是另一个使用 System.Threading.Timer
的例子:
public sealed class MyTimedBackgroundService : IHostedService
{
private Timer _t;
private static int MilliSecondsUntilMidnight()
{
return (int)(DateTime.Today.AddDays(1.0) - DateTime.Now).TotalMilliseconds;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// set up a timer to be non-reentrant
_t = new Timer(async _ => await OnTimerFiredAsync(cancellationToken),
null, MilliSecondsUntilMidnight(), Timeout.Infinite);
}
public Task StopAsync(CancellationToken cancellationToken)
{
_t?.Dispose();
return Task.CompletedTask;
}
private async Task OnTimerFiredAsync(CancellationToken cancellationToken)
{
try
{
// do your work here
Debug.WriteLine("Simulating heavy I/O bound work");
await Task.Delay(2000, cancellationToken);
}
finally
{
// set timer to fire off again
_t?.Change(MilliSecondsUntilMidnight(), Timeout.Infinite);
}
}
}
(此代码未经测试,可能存在一些spelling/syntax错误)
这是一个不可重入计时器,这意味着如果它当前正在处理数据,您可以保证它不会再次触发。
它将计算到午夜的毫秒数,然后根据该计算设置计时器。
这个想法被采纳了 from Microsoft。
这两个例子都可以这样注入:
services.AddHostedService<MyTimedBackgroundService>();
云原生警告:
请记住,由于这些示例是您的应用程序的本地示例,如果您的应用程序在您拥有多个实例的情况下水平扩展 运行,您将 运行 两个或更多个定时器,只是在不同的进程中。只是一个善意的提醒。如果您的应用程序永远不会扩展,请忽略此警告。
我需要将几个计划任务从 widows 服务器的计划程序任务转换为使用 .Net 的独立应用程序。
过去,我在 .Net 框架上使用过 Quartz 4.x,在基于不同调度程序的多个长 运行ning 任务中遇到一些小问题。
现在我正在使用 .Net 5,我想知道是否有一种新的方式来安排任务,比如工作人员服务,或者它仍然更好更灵活地使用 Quartz.Net。
由于我需要 运行 长时间任务,从 30 秒到 2 小时,我需要创建一个定时后台任务,使用 System.Threading.Timer
代码应该如下:
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");
_timer = new Timer(DoSomething, null, TimeSpan.Zero,
TimeSpan.FromHours(24));
return Task.CompletedTask;
}
它应该每 24 小时调用一次 DoSomething。
我的疑惑是:
- 它什么时候开始工作并计算 24 小时,当我第一次 运行 应用程序?
- 怎么说任务必须运行在 一天中的特定时间,例如午夜?
- Worker Service适合管理定时任务吗?
When does it start to work and to count the 24hrs, when I first run the application?
是的。设置断点并启动您的应用程序。你会看到它触发的速度有多快。
Is the Worker Service suitable for managing scheduled tasks?
是的。
How can I say that the task must be run at a specific time in a day, at midnight for example?
让我们看一下这段代码:
public sealed class MyTimedBackgroundService : BackgroundService
{
private static int SecondsUntilMidnight()
{
return (int)(DateTime.Today.AddDays(1.0) - DateTime.Now).TotalSeconds;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var countdown = SecondsUntilMidnight();
while (!stoppingToken.IsCancellationRequested)
{
if (countdown-- <= 0)
{
try
{
await OnTimerFiredAsync(stoppingToken);
}
catch(Exception ex)
{
// TODO: log exception
}
finally
{
countdown = SecondsUntilMidnight();
}
}
await Task.Delay(1000, stoppingToken);
}
}
private async Task OnTimerFiredAsync(CancellationToken stoppingToken)
{
// do your work here
Debug.WriteLine("Simulating heavy I/O bound work");
await Task.Delay(2000, stoppingToken);
}
}
这根本不使用 System.Threading.Timer
,因为您担心计时器由于某些边界而从未真正触发。有些人对此很偏执。我从未遇到过这种事。我经常使用 Timer
进行此类工作。
它将计算到午夜的秒数然后循环直到它到达那里。
这是一个不可重入的定时器,由于处理延迟的业务逻辑,会有轻微的时间滑移。
这是另一个使用 System.Threading.Timer
的例子:
public sealed class MyTimedBackgroundService : IHostedService
{
private Timer _t;
private static int MilliSecondsUntilMidnight()
{
return (int)(DateTime.Today.AddDays(1.0) - DateTime.Now).TotalMilliseconds;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// set up a timer to be non-reentrant
_t = new Timer(async _ => await OnTimerFiredAsync(cancellationToken),
null, MilliSecondsUntilMidnight(), Timeout.Infinite);
}
public Task StopAsync(CancellationToken cancellationToken)
{
_t?.Dispose();
return Task.CompletedTask;
}
private async Task OnTimerFiredAsync(CancellationToken cancellationToken)
{
try
{
// do your work here
Debug.WriteLine("Simulating heavy I/O bound work");
await Task.Delay(2000, cancellationToken);
}
finally
{
// set timer to fire off again
_t?.Change(MilliSecondsUntilMidnight(), Timeout.Infinite);
}
}
}
(此代码未经测试,可能存在一些spelling/syntax错误)
这是一个不可重入计时器,这意味着如果它当前正在处理数据,您可以保证它不会再次触发。
它将计算到午夜的毫秒数,然后根据该计算设置计时器。
这个想法被采纳了 from Microsoft。
这两个例子都可以这样注入:
services.AddHostedService<MyTimedBackgroundService>();
云原生警告:
请记住,由于这些示例是您的应用程序的本地示例,如果您的应用程序在您拥有多个实例的情况下水平扩展 运行,您将 运行 两个或更多个定时器,只是在不同的进程中。只是一个善意的提醒。如果您的应用程序永远不会扩展,请忽略此警告。