.NET Core IHostedService 立即抛出 "A task was canceled"

.NET Core IHostedService throws "A task was canceled" immediately

我正在尝试在每小时运行一次的 .NET Core 3.1 应用程序中构建一项服务。我的服务每小时运行一次(即在 StartAsync() 中遇到断点),但实施立即抛出以下错误:

A task was canceled

我搜索了很多,但我很难找到有用的回复;似乎这些是在使用 HttpClient 时最常抛出的,这与我的情况无关。

就是说,我有一种预感,我应该等待 某事 ,但我尝试添加 await 导致应用程序在启动时挂起(我想我无意中等待了完整的每小时迭代......?)。 tutorial I've been following 不会在任何地方使用 await;除非有疏忽(很有可能),否则我的代码似乎与所示示例相同。

我已经取出了 ExecuteAll() 的内容来确认作品中的代码。

谢谢。

public Task StartAsync(CancellationToken cancellationToken)
{
  TimeSpan interval = TimeSpan.FromHours(1);
  var nextRunTime = DateTime.Today.AddHours(DateTime.UtcNow.Hour + 1);

  var curTime = DateTime.UtcNow;
  var firstInterval = nextRunTime.Subtract(curTime);

  //Run once on startup
  TriggerHourlyService(null);
  void action()
  {
    //Schedule the first iteration
    var t1 = Task.Delay(firstInterval);
    t1.Wait();
    TriggerHourlyService(null);

    //Schedule hourly
    _timer = new Timer(
      TriggerHourlyService,
      null,
      TimeSpan.Zero,
      interval
    );
  }

  Task.Run(action);
  return Task.CompletedTask;
}
private void TriggerHourlyService(object state)
{
  using var scope = _serviceScopeFactory.CreateScope();
  IHourlyService hourlyService = scope.ServiceProvider.GetRequiredService<IHourlyService>();

  //ASYNC METHOD
  hourlyService.ExecuteAll();
}

更新

根据评论的一些注释,我重写了一些代码以脱离教程(它似乎在提倡一些危险的做法)。然而,通过这次重写,我的应用程序在启动时挂起。我相信是 await Task.Delay(...) 导致了这个问题,但我的印象是这是非阻塞的。

public async Task StartAsync(CancellationToken cancellationToken)
{
    TimeSpan interval = TimeSpan.FromHours(1);
    var nextRunTime = DateTime.UtcNow.Date.AddHours(DateTime.UtcNow.Hour + 1);

    var curTime = DateTime.UtcNow;
    var firstInterval = nextRunTime.Subtract(curTime);

    //Run once on startup
    await TriggerHourlyService(null);

    //First run on next hour
    await Task.Delay(firstInterval);
    await TriggerHourlyService(null);

    //Schedule hourly
    _timer = new Timer(
        async (state) => await TriggerHourlyService(state),
        null,
        TimeSpan.Zero,
        interval
    );
}
private async Task TriggerHourlyService(object state)
{
    using var scope = _serviceScopeFactory.CreateScope();
    IHourlyService hourlyService = scope.ServiceProvider.GetRequiredService<IHourlyService>();
    await hourlyService.ExecuteAll();
}
如果您想在宿主程序启动和停止时 运行 编码,

IHostedServce 非常有用。您定义的每个服务将按照您定义的顺序启动和停止。但是如果您的启动方法永远不会完成,主机应用程序将永远不会启动。

如果您想要的只是一个长 运行ning 循环,BackgroundService 会为您处理启动/停止生命周期。

public class MyService : BackgroundService{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken){
        while(!stoppingToken.IsCancellationRequested){
            await Task.Delay(...., stoppingToken);
            //
        }
    }
}

请注意,如果您从来没有 await 未完成的任务,您的服务仍然可以阻止主机的启动。