很多时候我们都会使用后台定时任务,但有些任务不需要定时执行,只需要请求到来时执行一次,比如请求服务器到某个地方同步数据,但请求不需要等数据同步完成再响应。这时候就可以使用排队的后台任务。
基本原理是用一个队列保存任务委托,然后用一个后台定时任务依次执行队列中的委托。
MSDN上把源代码都写好了
1 public interface IBackgroundTaskQueue 2 { 3 ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken, ValueTask> workItem); 4 5 ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync( 6 CancellationToken cancellationToken); 7 } 8 9 public class BackgroundTaskQueue : IBackgroundTaskQueue 10 { 11 private readonly Channel<Func<CancellationToken, ValueTask>> _queue; 12 13 public BackgroundTaskQueue(int capacity) 14 { 15 // Capacity should be set based on the expected application load and 16 // number of concurrent threads accessing the queue. 17 // BoundedChannelFullMode.Wait will cause calls to WriteAsync() to return a task, 18 // which completes only when space became available. This leads to backpressure, 19 // in case too many publishers/calls start accumulating. 20 var options = new BoundedChannelOptions(capacity) 21 { 22 FullMode = BoundedChannelFullMode.Wait 23 }; 24 _queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options); 25 } 26 27 public async ValueTask QueueBackgroundWorkItemAsync( 28 Func<CancellationToken, ValueTask> workItem) 29 { 30 if (workItem == null) 31 { 32 throw new ArgumentNullException(nameof(workItem)); 33 } 34 35 await _queue.Writer.WriteAsync(workItem); 36 } 37 38 public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync( 39 CancellationToken cancellationToken) 40 { 41 var workItem = await _queue.Reader.ReadAsync(cancellationToken); 42 43 return workItem; 44 } 45 }BackgroundTaskQueue
1 public class QueuedHostedService : BackgroundService 2 { 3 private readonly ILogger<QueuedHostedService> _logger; 4 5 public QueuedHostedService(IBackgroundTaskQueue taskQueue, 6 ILogger<QueuedHostedService> logger) 7 { 8 TaskQueue = taskQueue; 9 _logger = logger; 10 } 11 12 public IBackgroundTaskQueue TaskQueue { get; } 13 14 protected override async Task ExecuteAsync(CancellationToken stoppingToken) 15 { 16 _logger.LogInformation( 17 $"Queued Hosted Service is running.{Environment.NewLine}" + 18 $"{Environment.NewLine}Tap W to add a work item to the " + 19 $"background queue.{Environment.NewLine}"); 20 21 await BackgroundProcessing(stoppingToken); 22 } 23 24 private async Task BackgroundProcessing(CancellationToken stoppingToken) 25 { 26 while (!stoppingToken.IsCancellationRequested) 27 { 28 var workItem = 29 await TaskQueue.DequeueAsync(stoppingToken); 30 31 try 32 { 33 await workItem(stoppingToken); 34 } 35 catch (Exception ex) 36 { 37 _logger.LogError(ex, 38 "Error occurred executing {WorkItem}.", nameof(workItem)); 39 } 40 } 41 } 42 43 public override async Task StopAsync(CancellationToken stoppingToken) 44 { 45 _logger.LogInformation("Queued Hosted Service is stopping."); 46 47 await base.StopAsync(stoppingToken); 48 } 49 }QueuedHostedService
services.AddHostedService<QueuedHostedService>(); services.AddSingleton<IBackgroundTaskQueue>(ctx => { int queueCapacity = 100; return new BackgroundTaskQueue(queueCapacity); });
然后在控制器的方法中向队列中添加委托即可
await _backgroundTaskQueue.QueueBackgroundWorkItemAsync(async token => { await SyncPersonPhotoAsync(persons); });
标签:webapi,workItem,logger,stoppingToken,await,动态创建,async,后台任务,public From: https://www.cnblogs.com/ggtc/p/18165246