如何使用取消令牌停止范围服务?

Posted

技术标签:

【中文标题】如何使用取消令牌停止范围服务?【英文标题】:How to stop scoped service using Cancellation Token? 【发布时间】:2021-05-13 22:03:52 【问题描述】:

我正在寻找一种使用Cancellation Token 来阻止Scoped background service 的方法。我按照以下步骤操作:

ScopedAlertingService.cs

namespace BackgroundTasksSample.Services

    internal interface IScopedAlertingService
    
        Task DoWork(System.Threading.CancellationToken stoppingToken);
    
    public class ScopedAlertingService : IScopedAlertingService
    
        private int executionCount = 0;
        private readonly ILogger _logger;

        public ScopedAlertingService(ILogger<ScopedAlertingService> logger)
        
            _logger = logger;
        

        public async Task DoWork(System.Threading.CancellationToken stoppingToken)
        
            while (!stoppingToken.IsCancellationRequested)
            
                executionCount++;
                _logger.LogInformation(
                    "Alert: Scoped Processing Service is working. Count: Count", executionCount);
            
        
    

ConsumedServiceHostedService.cs

namespace BackgroundTasksSample.Services

    public class ConsumeScopedServiceHostedService : BackgroundService
    
        private readonly ILogger<ConsumeScopedServiceHostedService> _logger;

        public ConsumeScopedServiceHostedService(IServiceProvider services,
            ILogger<ConsumeScopedServiceHostedService> logger)
        
            Services = services;
            _logger = logger;
        

        public IServiceProvider Services  get; 


        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        
            CancellationTokenSource _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
            _stoppingCts.CancelAfter(30000);

            _logger.LogInformation(
                "Consume Scoped Service Hosted Service running.");

            while (!stoppingToken.IsCancellationRequested)
            
                await Task.Delay(10000, stoppingToken);

                using (var scope = Services.CreateScope())
                
                    var scopedProcessingService =
                        scope.ServiceProvider
                            .GetRequiredService<IScopedAlertingService>();

                    
                    await scopedProcessingService.DoWork(stoppingToken);
                
            
    

        private async Task DoWork(CancellationToken stoppingToken)
        
            _logger.LogInformation(
                "Consume Scoped Service Hosted Service is working.");

            using (var scope = Services.CreateScope())
            
                var scopedProcessingService =
                    scope.ServiceProvider
                        .GetRequiredService<IScopedAlertingService>();

                await scopedProcessingService.DoWork(stoppingToken);
            
        

        public override async Task StopAsync(CancellationToken stoppingToken)
        
            _logger.LogInformation(
                "Consume Scoped Service Hosted Service is stopping.");

            await Task.CompletedTask;
        
    
    

我正在使用 IServiceCollection 运行此服务

#region snippet2
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedAlertingService, ScopedAlertingService>();
#endregion

我正在使用以下代码在 30 秒后停止服务:

 CancellationTokenSource _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
 _stoppingCts.CancelAfter(30000); 

但是调试器不会去ConsumeScopedServiceHostedServicestopAsync方法。我在这里做错了什么?谢谢

【问题讨论】:

你怎么证明没有停止?我的意思是您的日志中将包含数百万个事件executionCount++; _logger.LogInformation(" 您只是在尽可能快地在循环中破坏 cpu 核心。我认为你需要重新考虑你的测试 linked token sources 的目的与您所做的相反。链接 CTS 旨在监听链接令牌中的取消,而不是宣传取消。 @00110001 因为我放了调试器,还尝试添加console.writeline。测试用例工作正常。我正在寻找一种在 30 秒后停止服务的方法,我可以看到计数器超过 100 秒。所以,当我检查我的控制台时。它不在那里。 (在上面的代码中也添加了console.writeline) @Nick 啊!你能告诉我应该如何停止服务或如何在这里传递停止的令牌吗? 您正在创建链接令牌源而不使用其令牌?本质上你只是忽略它 【参考方案1】:

您可以将IHostedService 注入为Singleton 然后您可以在后台服务上调用停止方法。

更新: 在托管服务中,stopasync 仅在应用程序关闭时调用,但在您的代码中您可以使用它并且它可以工作:

 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        
            CancellationTokenSource _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
            _stoppingCts.CancelAfter(30000);

            _logger.LogInformation(
                "Consume Scoped Service Hosted Service running.");

            while (!_stoppingCts.Token.IsCancellationRequested)
            
                await Task.Delay(10000, _stoppingCts.Token);

                using (var scope = Services.CreateScope())
                
                    var scopedProcessingService =
                        scope.ServiceProvider
                            .GetRequiredService<IScopedAlertingService>();

                    
                    await scopedProcessingService.DoWork(_stoppingCts.Token);
                
            
    

【讨论】:

感谢您的回答赛义德。这是哪个控制器?我需要创建一个新的控制器还是你在谈论ConsumeScopedServiceHostedService @Payal_Thakur 控制器或任何您想通过CancellationToken 的地方,如果您没有任何控制器(如果您的项目不是 Web 应用程序)。您可以执行选项 2。 我使用了第二个选项并在启动时更新了配置文件,但结果是一样的。 ` services.AddSingleton(); services.AddHostedService(); services.AddScoped();` 我认为它正在停止服务,但调试点没有到达StopAsync 方法。这不寻常吗?

以上是关于如何使用取消令牌停止范围服务?的主要内容,如果未能解决你的问题,请参考以下文章

使用任务取消令牌停止 TCP 侦听器

在 useEffect 挂钩中使用 axios 取消令牌时如何修复失败的测试

在服务结构服务中存储取消令牌

如何使用取消令牌Azure Service Bus(SubscriptionClient)

用户取消输入框后如何停止vba

服务器端 C# Websocket,如何在监听取消令牌时运行循环?