如何在C# .NET中获取Worker后台服务的运行结果?
在Worker Service运行完成后获取运行结果的方法
针对你提出的需求,以下是两种可行的实现方案:
方案一:给Worker类添加结果属性并提供线程安全的获取方法
直接修改Worker类,添加存储结果/异常的属性,并通过线程安全的方法对外暴露,避免多线程竞态问题:
public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private readonly SemaphoreSlim _resultLock = new SemaphoreSlim(1, 1); private string? _runResult; private Exception? _runException; private readonly IServiceProvider _services; public Worker(ILogger<Worker> logger, IServiceProvider services) { _logger = logger; _services = services; } // 线程安全的结果获取方法 public async Task<(string? Result, Exception? Exception)> GetRunResultAsync() { await _resultLock.WaitAsync(); try { return (_runResult, _runException); } finally { _resultLock.Release(); } } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); string result = "it worked, processed some stuff"; _logger.LogInformation(result); // 保存运行结果 await _resultLock.WaitAsync(); try { _runResult = result; } finally { _resultLock.Release(); } // 主动触发宿主关闭 var lifetime = _services.GetRequiredService<IHostApplicationLifetime>(); lifetime.StopApplication(); } catch (Exception ex) { _logger.LogError(ex, "Worker execution failed"); // 保存异常信息 await _resultLock.WaitAsync(); try { _runException = ex; } finally { _resultLock.Release(); } throw; } } }
修改Program的Main方法,改用异步方式运行宿主,待宿主关闭后获取Worker实例并读取结果:
public class Program { public static async Task Main(string[] args) { IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<Worker>(); }) .Build(); // 异步等待宿主运行结束 await host.RunAsync(); // 获取Worker实例并读取结果 var worker = host.Services.GetRequiredService<Worker>(); var (result, exception) = await worker.GetRunResultAsync(); if (exception != null) { Console.WriteLine($"Worker运行失败:{exception.Message}"); } else { Console.WriteLine($"Worker运行结果:{result}"); } } }
方案二:使用共享状态容器存储结果
如果不想修改Worker类,可以创建一个独立的状态容器,通过依赖注入让Worker和主程序共享:
首先定义状态容器类:
public class WorkerRunState { private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); public string? Result { get; private set; } public Exception? Exception { get; private set; } public async Task SetResultAsync(string result) { await _lock.WaitAsync(); try { Result = result; } finally { _lock.Release(); } } public async Task SetExceptionAsync(Exception exception) { await _lock.WaitAsync(); try { Exception = exception; } finally { _lock.Release(); } } }
注册服务并修改Worker类:
// Program中注册服务 services.AddSingleton<WorkerRunState>(); services.AddHostedService<Worker>(); // Worker类注入状态容器 public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private readonly WorkerRunState _runState; private readonly IHostApplicationLifetime _lifetime; public Worker(ILogger<Worker> logger, WorkerRunState runState, IHostApplicationLifetime lifetime) { _logger = logger; _runState = runState; _lifetime = lifetime; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); string result = "it worked, processed some stuff"; _logger.LogInformation(result); await _runState.SetResultAsync(result); _lifetime.StopApplication(); } catch (Exception ex) { _logger.LogError(ex, "Worker execution failed"); await _runState.SetExceptionAsync(ex); throw; } } }
修改Main方法读取状态:
public static async Task Main(string[] args) { IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddSingleton<WorkerRunState>(); services.AddHostedService<Worker>(); }) .Build(); await host.RunAsync(); var runState = host.Services.GetRequiredService<WorkerRunState>(); if (runState.Exception != null) { Console.WriteLine($"Worker运行失败:{runState.Exception.Message}"); } else { Console.WriteLine($"Worker运行结果:{runState.Result}"); } }
关键注意点
- 必须使用
host.RunAsync()而非host.Run():Run()是同步阻塞方法,无法在宿主关闭后保留服务实例的可访问性;RunAsync()支持异步等待,宿主关闭后仍可通过host.Services获取实例。 - 线程安全处理:Worker在后台线程执行,主程序在宿主关闭后读取结果,必须用同步机制(如
SemaphoreSlim)避免竞态条件。
内容的提问来源于stack exchange,提问作者danrockcoll
相关产品推荐
相关产品推荐

