如何使用Task通过计数器运行BackgroundService?

问题描述 投票:0回答:1

我的 dot net core 应用程序中有一个后台服务。这将通过容量计数器运行。

public class WebWorkGenerator : BackgroundService
{
    private const int ConsumeCapacityPerMinute = 10;
    private  int _capacityCounter;
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            while (ConsumeCapacityPerMinute - _capacityCounter > 0)
            {
                _capacityCounter++;

                Console.WriteLine($"Working {_capacityCounter}/{ConsumeCapacityPerMinute}");

                Task.Run(ProcessItems,stoppingToken);
            }
        }

        await Task.CompletedTask;
    }

    private async Task ProcessItems()
    {
        await Task.Delay(new Random().Next(1000, 5000));
      
        _capacityCounter--;
        
        Console.WriteLine("Processed.");
    }

} 

后台作业将运行到 10,因为 ProcessItem() 向另一个服务发送请求,其容量为 10。

ProcessItems() 如果项目被处理则减少计数器。但我的回答如下:

Working 1/10
Working 2/10
Working 3/10
Working 4/10
Working 5/10
Working 6/10
Working 7/10
Working 8/10
Working 9/10
Working 10/10

还做其他事情。但是当

Task.Run(ProcessItems,stoppingToken);
执行时,
_capacityCounter
应该减少,而 while 循环必须继续。 怎样才能发挥作用呢?

c# .net-core task-parallel-library background-service
1个回答
0
投票

后台服务中

_capacityCounter
未正确递减的问题是由于主循环和生成的任务之间缺乏正确的同步而引起的。

一个可能的解决方案是手动同步

_capacityCounter
,尽管我会推荐像
SempahoreSlim
这样的第三方库来执行此操作。

public class WebWorkGenerator : BackgroundService
{
    private const int ConsumeCapacityPerMinute = 10;
    private readonly object _lock = new object();
    private int _capacityCounter;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            while (ConsumeCapacityPerMinute - _capacityCounter > 0)
            {
                lock (_lock)
                {
                    if (ConsumeCapacityPerMinute - _capacityCounter > 0)
                    {
                        _capacityCounter++;
                        Console.WriteLine($"Working {_capacityCounter}/{ConsumeCapacityPerMinute}");
                        Task.Run(ProcessItems, stoppingToken);
                    }
                }
            }

            await Task.Delay(100); // Introduce a small delay to avoid busy waiting
        }

        await Task.CompletedTask;
    }

    private void ProcessItems()
    {
        await Task.Delay(new Random().Next(1000, 5000));
        Console.WriteLine("Processed.");

        lock (_lock)
        {
            _capacityCounter--;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.