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

.NET Core中如何在BackgroundService中每周重启ThreadPool线程

需求:每周日自动重启数据采集线程

现有代码通过ThreadPool.QueueUserWorkItem启动数据采集接口,由HostedServiceManager后台服务触发启动。当前功能运行正常,需要实现每周日自动终止当前线程并重启,无需重启整个程序。


现有代码

1. InterfaceCommunicator

public static class InterfaceCommunicator
{
    public static void Start()
    {
        ThreadPool.QueueUserWorkItem(StartDataCollection);
    }

    private static void StartDataCollection(object state)
    {
        var @interface = new Interface(
            ConfigurationManager.AppSetting["CheckTool:ip"],
            ConfigurationManager.AppSetting["CheckTool:user_name"],
            ConfigurationManager.AppSetting["CheckTool:password"]);

        @interface.StartInterface();
    }
}

注:原代码中interface是C#关键字,已改为@interface避免编译错误

2. HostedServiceManager

public class HostedServiceManager : BackgroundService
{
    private readonly ILogger<HostedServiceManager> _logger;

    public HostedServiceManager(IServiceProvider services,
        ILogger<HostedServiceManager> logger)
    {
        Services = services;
        _logger = logger;
    }

    public IServiceProvider Services { get; }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("HostedServiceManager running.");

        await DoWork(stoppingToken);
    }

    private async Task DoWork(CancellationToken stoppingToken)
    {
        _logger.LogInformation("HostedServiceManager is working.");

        using (var scope = Services.CreateScope())
        {
            InterfaceCommunicator.Start();
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("HostedServiceManager is stopping.");

        await base.StopAsync(stoppingToken);
    }
}

实现方案

要实现定时重启,需要解决三个核心问题:线程可终止、定时触发重启、重启逻辑闭环。

1. 改造InterfaceCommunicator,支持线程终止

原线程无控制手段,需给Interface类添加终止能力,同时在InterfaceCommunicator中维护当前运行任务的引用:

public static class InterfaceCommunicator
{
    private static CancellationTokenSource _cts;
    private static Task _dataCollectionTask;

    public static void Start()
    {
        // 若已有运行任务,先终止
        Stop();
        
        _cts = new CancellationTokenSource();
        _dataCollectionTask = Task.Run(async () =>
        {
            try
            {
                var @interface = new Interface(
                    ConfigurationManager.AppSetting["CheckTool:ip"],
                    ConfigurationManager.AppSetting["CheckTool:user_name"],
                    ConfigurationManager.AppSetting["CheckTool:password"]);
                
                // 需同步改造Interface类,让StartInterface支持异步和取消令牌
                await @interface.StartInterfaceAsync(_cts.Token);
            }
            catch (OperationCanceledException)
            {
                _logger.LogInformation("数据采集线程已正常终止");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "数据采集线程异常终止");
            }
        }, _cts.Token);
    }

    public static void Stop()
    {
        if (_cts != null)
        {
            _cts.Cancel();
            _cts.Dispose();
            _cts = null;
        }
        
        // 等待任务结束(可选,根据业务需求调整超时时间)
        if (_dataCollectionTask != null && !_dataCollectionTask.IsCompleted)
        {
            _dataCollectionTask.Wait(TimeSpan.FromSeconds(10));
        }
    }
}

关键改造点:

  • 用Task.Run替代ThreadPool.QueueUserWorkItem,便于任务生命周期管理
    新增CancellationTokenSource实现优雅终止
  • 新增Stop()方法主动终止线程
  • 必须同步改造Interface类,让StartInterface支持异步和取消令牌(若原方法是同步阻塞,需拆分为可取消的异步逻辑,禁止使用Thread.Abort强制终止)

2. 在HostedService中实现定时触发逻辑

利用BackgroundService的循环特性,结合时间计算实现每周日自动重启:

public class HostedServiceManager : BackgroundService
{
    private readonly ILogger<HostedServiceManager> _logger;

    public HostedServiceManager(IServiceProvider services,
        ILogger<HostedServiceManager> logger)
    {
        Services = services;
        _logger = logger;
    }

    public IServiceProvider Services { get; }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("HostedServiceManager running.");

        // 启动初始数据采集任务
        StartDataCollection();

        while (!stoppingToken.IsCancellationRequested)
        {
            // 计算下一次周日凌晨2点的重启时间(可自定义时段)
            var nextRestartTime = GetNextSundayRestartTime(2);
            var delay = nextRestartTime - DateTime.Now;
            
            _logger.LogInformation("下次重启时间:{NextRestartTime}", nextRestartTime);

            // 等待到重启时间,或服务停止
            await Task.Delay(delay, stoppingToken);

            if (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("开始重启数据采集线程");
                // 终止旧线程并重启
                InterfaceCommunicator.Stop();
                await Task.Delay(TimeSpan.FromSeconds(2)); // 短暂等待确保资源释放
                StartDataCollection();
            }
        }
    }

    private void StartDataCollection()
    {
        using (var scope = Services.CreateScope())
        {
            InterfaceCommunicator.Start();
        }
    }

    // 计算下一次周日指定小时的时间
    private DateTime GetNextSundayRestartTime(int hour)
    {
        var now = DateTime.Now;
        var daysUntilSunday = ((int)DayOfWeek.Sunday - (int)now.DayOfWeek + 7) % 7;
        
        // 如果今天是周日且当前时间已过指定小时,取下周周日
        if (daysUntilSunday == 0 && now.Hour >= hour)
        {
            daysUntilSunday = 7;
        }

        return now.AddDays(daysUntilSunday).Date.AddHours(hour);
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("HostedServiceManager is stopping.");
        
        // 停止数据采集线程,避免资源泄漏
        InterfaceCommunicator.Stop();
        
        await base.StopAsync(stoppingToken);
    }
}

关键改造点:

  • 将ExecuteAsync改为循环模式,持续监控定时任务
  • 新增GetNextSundayRestartTime方法灵活计算重启时间
  • 服务停止时主动终止数据采集线程
  • 重启过程添加短暂延迟,确保旧线程资源完全释放

3. 补充说明

  • 若原Interface.StartInterface是同步阻塞方法,必须改造为支持取消的异步方法,否则无法优雅终止线程
  • 重启时段可通过GetNextSundayRestartTime的参数自定义(如周日凌晨3点)
  • 可添加更详细的日志记录重启过程,便于排查问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 13:40:26