.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
相关产品推荐
相关产品推荐

