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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 18:05:22