.NET Core IHostedService 抛出“一个任务被取消";立即地 [英] .NET Core IHostedService throws "A task was canceled" immediately

查看:27
本文介绍了.NET Core IHostedService 抛出“一个任务被取消";立即地的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to build a service into my .NET Core 3.1 application that runs hourly. My service runs each hour (i.e. hits a breakpoint in StartAsync()), but the implementation throws the following error immediately:

任务被取消

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

I've searched quite a bit, however I'm having difficulty finding helpful responses; it seems these are most commonly thrown when using HttpClient, which is not relevant to my case.

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

That said, I have a hunch that I should be awaiting something, but my attempts to add await resulted in the application hanging on startup (I imagine I was unintentionally awaiting the full hourly iteration...?). The tutorial I've been following does not use await anywhere; barring an oversight (very possible), my code seems identical to the example shown.

我已经拿出 ExecuteAll() 的胆量来确认工作中的代码.

I've pulled out the guts of ExecuteAll() to confirm the code within works.

谢谢.

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(...) 导致了这个问题,但我的印象是这是非阻塞的.

Update

After some notes from the comments, I've rewritten the code a bit to move away from the tutorial (it seems it was promoting some dangerous practices). However with this rewrite, my application hangs on startup. I believe it's the await Task.Delay(...) causing the issue, but I was under the impression this was non-blocking.

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 如果您想在主机程序启动和停止时运行代码,则非常有用.您定义的每个服务都将按照您定义的顺序启动和停止.但是,如果您的 start 方法永远不会完成,则宿主应用程序将永远不会启动.

IHostedServce is great if you want to run code while your host program is starting and stopping. Each service you have defined will be started and stopped in the order you define them. But if your start method never completes, the host application will never start.

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

If all you want is a long running loop, BackgroundService takes care of the start / stop lifetime for you.

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

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

Note that your service can still block the startup of the host if you never await an incomplete task.

这篇关于.NET Core IHostedService 抛出“一个任务被取消";立即地的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆