簡體   English   中英

.Net core IHostedService 后台任務異常不會終止應用程序

[英].Net core IHostedService Background task exception will not terminate application

我有一個程序需要在 IHostedService 后台任務遇到某種情況時終止。 我希望通過在后台任務中拋出一個異常來實現這一點,該異常將被踢到主函數。 然后我可以觸發取消令牌來終止其他后台任務。 我的問題是,當我拋出異常時,它會終止任務,僅此而已。 其他一切都在繼續運行。 有沒有辦法做到這一點,或者有更好的方法來做我想做的事情? 是否有另一種方法可以讓后台任務觸發通用的 CancellationToken?

我在下面的代碼中包含了我的問題的簡化版本。 如果我注釋掉await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); 行,異常做了我想要的,我可以觸發 CancellationToken。 當它就位時,任務會停止,但程序不會。

注意:在我更混亂的代碼中,我有更多的 IHostedServices 正在運行,這就是為什么我試圖觸發cancelSource.Cancel()

public class Program
{
    public static async Task Main(string[] args)
    {
        using (var cancelSource = new CancellationTokenSource())
        {
            try
            {
                await new HostBuilder()
                    .ConfigureServices((hostContext, services) =>
                    {
                        services.AddHostedService<TestService>();
                    })
                    .Build()
                    .RunAsync(cancelSource.Token);
            }
            catch (Exception E)
            {
                cancelSource.Cancel();
            }
        }
    }
}
public class TestService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {

        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
            Console.WriteLine("loop 1");
            throw new ApplicationException("OOPS!!");
        }
    }
}

使用await運算符注釋ExecuteAsync方法中的唯一行會使您的代碼同步運行。 如果我們查看BackgroundService.StartAsync的來源,我們可以看到它檢查_executingTask.IsCompleted並返回包含異常的任務,以防我們在ExecuteAsync方法中沒有任何 await,否則它將返回Task.CompletedTask和您將無法從Main方法中的ExecuteAsync捕獲此異常。

您可以使用ApplicationLifetime管理您的服務,該服務可以注入所有后台服務,例如您可以在ExecuteMethod捕獲異常並調用ApplicationLifetime.StopApplication

例子:

static async Task Main(string[] args)
{
    await new HostBuilder()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<TestService>();
            services.AddHostedService<TestService2>();
        })
        .Build()
        .RunAsync();

    Console.WriteLine("App stoped");
}

服務1

public class TestService : BackgroundService
{
    private readonly IApplicationLifetime _applicationLifetime;
    public TestService(IApplicationLifetime applicationLifetime)
    {
        _applicationLifetime = applicationLifetime;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!_applicationLifetime.ApplicationStopping.IsCancellationRequested)
            {
                await Task.Delay(TimeSpan.FromSeconds(1), _applicationLifetime.ApplicationStopping);
                Console.WriteLine("running service 1");
                throw new ApplicationException("OOPS!!");
            }
        }
        catch (ApplicationException)
        {
            _applicationLifetime.StopApplication();
        }
    }
}

服務2

public class TestService2 : BackgroundService
{
    private readonly IApplicationLifetime _applicationLifetime;
    public TestService2(IApplicationLifetime applicationLifetime)
    {
        _applicationLifetime = applicationLifetime;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!_applicationLifetime.ApplicationStopping.IsCancellationRequested)
            {
                await Task.Delay(100, _applicationLifetime.ApplicationStopping);
                Console.WriteLine("running service 2");
            }
        }
        catch (ApplicationException)
        {
            _applicationLifetime.StopApplication();
        }
    }
}

輸出:

running service 2
running service 2
running service 1
App stoped

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM