【问题标题】:.NET Core IHostedService throws "A task was canceled" immediately.NET Core IHostedService 立即抛出“任务已取消”
【发布时间】:2021-07-20 07:50:33
【问题描述】:

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

任务被取消

我已经搜索了很多,但是我很难找到有用的答案;使用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();
}

更新

在收到 cmets 的一些注释后,我稍微重写了代码以远离本教程(它似乎在宣传一些危险的做法)。但是,通过这种重写,我的应用程序在启动时挂起。我相信是 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();
}

【问题讨论】:

  • 告诉我更多关于您的服务的信息。您有一个连接到服务的客户端应用程序。告诉我客户端如何与服务通信?你有一个请求和一个响应。响应是
  • 您能否详细说明或更直接地说明您的建议?我感觉有些隐晦的建议我没有接受。
  • 我不想反对你。你说你没有HttpClient。您有一个客户端与服务器通信。所以有一个协议是用来通信的。它可以是任何东西,但现在大多数情况下是 HTTP。所以我需要更多关于协议的信息。
  • 如果该方法 hourlyService.ExecuteAll(); 没有阻塞,那么您的服务提供者和由它创建的所有内容都会在该方法内部的任何内容运行之前被处置。此外,您的代码可能会通过调用:t1.Wait(); 陷入死锁。你应该重建那块,这样你就可以正确地await它。
  • @Santi -- IHostedService 被注入 as transient

标签: c# .net-core asp.net-core-hosted-services


【解决方案1】:

IHostedServce 非常适合您在主机程序启动和停止时运行代码。您定义的每个服务都将按照您定义的顺序启动和停止。但是,如果您的启动方法永远不会完成,宿主应用程序将永远不会启动。

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

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

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

【讨论】:

    猜你喜欢
    • 2019-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多