You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IIS回收时如何确保DotNet Core的IHostedService执行完成

解决方案

核心思路是两层配合:一是正确实现托管服务的优雅停止逻辑,在收到关闭信号时等待正在运行的任务执行完成;二是调整IIS和托管模块的超时配置,给任务执行留出足够时间,避免进程被提前强杀。

1. 正确实现IHostedService的停止逻辑

不要直接在BackgroundService.ExecuteAsync中用不跟踪的Task.Run启动任务,必须保存正在运行的任务引用,在StopAsync方法中显式等待任务完成,同时配合取消令牌做超时兜底。
参考实现代码:

public class ScheduledJobHostedService : BackgroundService
{
    private readonly ILogger<ScheduledJobHostedService> _logger;
    // 持有当前运行中任务的引用
    private Task _inProgressTask = Task.CompletedTask;
    private CancellationTokenSource _jobCts = new();

    public ScheduledJobHostedService(ILogger<ScheduledJobHostedService> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken hostStoppingToken)
    {
        // 宿主停止时触发任务取消通知
        using var reg = hostStoppingToken.Register(() => _jobCts.Cancel());
        // 按配置的间隔启动定时调度
        var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
        
        try
        {
            while (await timer.WaitForNextTickAsync(hostStoppingToken))
            {
                // 避免任务重入:上一轮没跑完就跳过本次调度
                if (!_inProgressTask.IsCompleted)
                {
                    _logger.LogWarning("上轮计划任务未执行完成,跳过本次调度");
                    continue;
                }
                // 重置令牌源
                _jobCts.Dispose();
                _jobCts = new CancellationTokenSource();
                // 启动任务,暂不await,保存引用供停止时等待
                _inProgressTask = RunAllJobs(_jobCts.Token);
            }
        }
        catch (OperationCanceledException)
        {
            // 宿主正常停止,忽略取消异常
        }
    }

    private async Task RunAllJobs(CancellationToken jobCancellationToken)
    {
        try
        {
            // 依次执行你的计划任务,必须传入取消令牌
            // 长耗时步骤中要主动判断jobCancellationToken.IsCancellationRequested,及时做资源释放、事务回滚等收尾
            await FirstJob(jobCancellationToken);
            await SecondJob(jobCancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "计划任务执行抛出异常");
        }
    }

    public override async Task StopAsync(CancellationToken shutdownTimeoutToken)
    {
        // 先停止调度循环
        await base.StopAsync(shutdownTimeoutToken);
        
        try
        {
            // 等待正在运行的任务完成,超时令牌触发后会自动抛出取消异常
            await _inProgressTask.WaitAsync(shutdownTimeoutToken);
            _logger.LogInformation("运行中计划任务已全部执行完成,应用正常退出");
        }
        catch (OperationCanceledException)
        {
            _logger.LogError("应用关闭超时,计划任务未执行完成即被终止,请检查任务是否存在死锁或过长耗时");
        }
        finally
        {
            _jobCts.Dispose();
        }
    }
}

2. 调整IIS应用池关闭超时配置

IIS应用池默认给进程的优雅关闭时间只有90秒,如果你的计划任务最长耗时超过这个值,IIS会在超时后直接强制杀死w3wp进程,导致任务中断。

  • 打开IIS管理器,找到站点对应的应用程序池,打开「高级设置」
  • 找到关闭时间限制(秒)选项,设置为大于单任务最长执行时长的值,比如单任务最多跑3分钟就设为240,留足冗余
  • 建议同时把「启动32位应用程序」按你的部署需求设置正确,避免不必要的进程异常

3. 配置ASP.NET Core托管模块超时

IIS上托管.NET Core应用时,AspNetCore模块(ANCM)本身也有一层关闭超时限制,需要和应用池配置对齐:
找到站点发布目录下的web.config,修改<aspNetCore>节点,添加shutdownTimeLimit属性,值和应用池设置的秒数一致:

<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments="YourApp.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" shutdownTimeLimit="240" />
    </system.webServer>
  </location>
</configuration>

注意事项

  • 所有计划任务的代码必须支持取消:长循环、IO操作等步骤要主动传入取消令牌,收到取消信号后不要继续执行业务逻辑,尽快完成收尾操作,避免超时被强杀。
  • 禁止启动不受生命周期管理的后台任务:不要在托管服务中调用Task.Run、Thread.Start启动游离的后台线程/任务,这类任务不会被StopAsync跟踪,关闭时必然被中断。
  • 如果任务对可靠性要求极高(比如涉及资金、核心数据写入),不要依赖IIS托管的IHostedService运行,建议拆分到独立的Windows服务、后台作业调度系统中部署,避免IIS回收、站点异常等场景导致任务中断。
  • 如果任务不允许重复执行,建议在任务启动时加分布式锁/本地文件锁,因为IIS默认是重叠回收模式,新进程启动后旧进程才会开始关闭,极端情况下可能出现新旧进程同时跑同一任务的情况。

内容的提问来源于stack exchange,提问作者rst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:42:18