.NET 8 的 IHostedLifecycleService 接口是鸡肋功能吗?
创始人
2025-07-10 06:50:36
0

.NET 8 引入了一个新的接口,叫做IHostedLifecycleService,这个接口继承自现有的 IHostedService 接口,它为 BackgroundService 提供了一些新的生命周期事件的方法:

  • StartingAsync:在 StartAsync 方法之前执行,用于执行一些初始化或预处理的逻辑。
  • StartedAsync:在 StartAsync 方法之后执行,用于执行一些后处理或检查的逻辑。
  • StoppingAsync:在 StopAsync 方法之前执行,用于执行一些清理或释放的逻辑。
  • StoppedAsync:在 StopAsync 方法之后执行,用于执行一些收尾或报告的逻辑。

这些方法都发生在现有的 StartAsync 和 StopAsync 方法之前或之后。

示例代码

下面的示例演示如何使用新 API:

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService();

var host = builder.Build();
host.Run();

public class MyIOWorker : BackgroundService, IHostedLifecycleService
{
    public async Task StartingAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"{nameof(MyIOWorker)} Starting");//业务逻辑
    }
    public async Task StartedAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"{nameof(MyIOWorker)} Started");//业务逻辑
    }
    public async Task StoppingAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"{nameof(MyIOWorker)} Stopping");//业务逻辑
    }
    public async Task StoppedAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"{nameof(MyIOWorker)} Stopped");//业务逻辑
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine($"{nameof(MyIOWorker)} Execute");//业务逻辑
            await Task.Delay(1000, stoppingToken);
        }
    }
}

输出结果如下:

MyIOService Starting
MyIOService Execute
MyIOService Started

...

MyIOService Stopping
MyIOService Stopped

鸡肋功能?

但是,直接使用 IHostedService 接口一样可以实现相同功能:

public class MyIOWorker : BackgroundService
{ 
    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"{nameof(MyIOWorker)} Starting");//业务逻辑
        await base.StartAsync(cancellationToken);
        Console.WriteLine($"{nameof(MyIOWorker)} Started");//业务逻辑
    }
     
    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"{nameof(MyIOWorker)} Stopping");//业务逻辑
        await base.StopAsync(cancellationToken);
        Console.WriteLine($"{nameof(MyIOWorker)} Stopped");//业务逻辑
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine($"{nameof(MyIOWorker)} ExecuteAsync");//业务逻辑
            await Task.Delay(1000, stoppingToken);
        }
    }
}

那么,新特性IHostedLifecycleService的意义何在呢?

仅仅为了,方便放置不同逻辑的代码吗?

探究源码

在dotnet/runtime源码https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs中,我们找到了 IHostedLifecycleService 的使用逻辑:

// Call StartingAsync().
if (_hostedLifecycleServices is not null)
{
    await ForeachService(_hostedLifecycleServices, cancellationToken, concurrent, abortOnFirstException, exceptions,
        (service, token) => service.StartingAsync(token)).ConfigureAwait(false);

    // Exceptions in StartingAsync cause startup to be aborted.
    LogAndRethrow();
}

// Call StartAsync().
await ForeachService(_hostedServices, cancellationToken, concurrent, abortOnFirstException, exceptions,
    async (service, token) =>
    {
        await service.StartAsync(token).ConfigureAwait(false);

        if (service is BackgroundService backgroundService)
        {
            _ = TryExecuteBackgroundServiceAsync(backgroundService);
        }
    }).ConfigureAwait(false);

// Exceptions in StartAsync cause startup to be aborted.
LogAndRethrow();

// Call StartedAsync().
if (_hostedLifecycleServices is not null)
{
    await ForeachService(_hostedLifecycleServices, cancellationToken, concurrent, abortOnFirstException, exceptions,
        (service, token) => service.StartedAsync(token)).ConfigureAwait(false);
}

上面的代码先遍历执行IEnumerable? _hostedLifecycleServices的StartingAsync方法,再遍历执行IEnumerable? _hostedServices的StartAsync方法。

也就是说,如果存在多个IHostedLifecycleService实现,我们可以把初始化代码放在StartingAsync方法实现中,保证了全部初始化逻辑执行成功后才会执行StartAsync方法中正式的业务逻辑。对于StopAsync方法也是同理。

使用场景

比如,如果直接使用 IHostedService 接口:

builder.Services.AddHostedService();
builder.Services.AddHostedService();

public class AWorker : BackgroundService
{ 
    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        //初始化数据库A表
    } 
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        //访问数据库A表和B表
    }
}

public class BWorker : BackgroundService
{ 
    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        //初始化数据库B表
    } 
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        //访问数据库A表和B表
    }
}

由于执行有先后顺序,初始化数据库B表操作还没有执行,AWorker 就已经开始执行ExecuteAsync方法了,AWorker 的访问数据库A表和B表操作可能产生不可预料的结果。

现在使用IHostedLifecycleService,将初始化放在生命周期的早期:

public class AWorker : BackgroundService, IHostedLifecycleService
{
    public async Task StartingAsync(CancellationToken cancellationToken)
    {
        //初始化数据库A表
    } 
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        //访问数据库A表和B表
    }
}

public class BWorker : BackgroundService, IHostedLifecycleService
{
    public async Task StartingAsync(CancellationToken cancellationToken)
    {
        //初始化数据库B表
    } 
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        //访问数据库A表和B表
    }
}

现在,访问数据库A表和B表操作可以保证正常执行了。

默认情况下,多个IHostedLifecycleService实现是按顺序执行的,我们还可以设置它们并发启动和停止,节约整体启动时间:

builder.Services.Configure(options =>
{
    options.ServicesStartConcurrently = true;
    options.ServicesStopConcurrently = true;
});

总结

IHostedLifecycleService是.NET 8中引入的一个新特性,它可以让我们在使用多个IHostedService实现的时候,更加灵活和高效地控制它们的启动和停止,避免出现不必要的依赖和冲突。

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
规避非法攻击 用好路由器远程管... 单位在市区不同位置设立了科技服务点,每一个服务点的员工都通过宽带路由器进行共享上网,和单位网络保持联...
范例解读VB.NET获取环境变... VB.NET编程语言的使用范围非常广泛,可以帮助开发人员处理各种程序中的需求,而且还能对移动设备进行...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...