问题描述
在我的网络应用程序中,我对长时间运行的任务进行了操作,我想在后台调用此任务。因此,根据文档 .net core 3.1 Queued background tasks 我为此使用了这样的代码:
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken,ValueTask> workItem);
ValueTask<Func<CancellationToken,ValueTask>> DequeueAsync(CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken,ValueTask>> _queue;
public BackgroundTaskQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity){FullMode = BoundedChannelFullMode.Wait};
_queue = Channel.CreateBounded<Func<CancellationToken,ValueTask>>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken,ValueTask> workItem)
{
if (workItem == null)throw new ArgumentNullException(nameof(workItem));
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask<Func<CancellationToken,ValueTask>> DequeueAsync(CancellationToken cancellationToken)
{
var workItem = await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
和托管服务
public class QueuedHostedService : BackgroundService
{
private readonly ILogger<QueuedHostedService> _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue,ILogger<QueuedHostedService> logger)
{
TaskQueue = taskQueue;
_logger = logger;
}
public IBackgroundTaskQueue TaskQueue { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await BackgroundProcessing(stoppingToken);
}
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(stoppingToken);
try
{
await workItem(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex,"Error occurred executing {WorkItem}.",nameof(workItem));
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.Log@R_556_4045@ion("Queued Hosted Service is stopping.");
await base.StopAsync(stoppingToken);
}
}
然后我注册所有服务
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue>(new BackgroundTaskQueue(queueCapacity));
然后我可以通过像这样的示例中那样不带参数地调用来成功使用它
public async Task<TenantBo> RegisterCompanyAsync(AddTenantBo addTenantBo)
{
var tenantBo = new TenantBo();
try
{
_companyRegistrationLogHelper.SetInfoLog(GetTenantId(tenantBo),"Start create company: " + JsonConvert.SerializeObject(addTenantBo));
InitOnCreateCompanyTasks(tenantBo);
//skip if already create tenant
tenantBo = await CreateTenantAsync(tenantBo,addTenantBo);
//run in background
_companyRegistationQueue.QueueBackgroundWorkItemAsync(RunRegistrationCompanyMainAsync);
return tenantBo;
}
catch (Exception e)
{
//some logs
return tenantBo;
}
}
private async ValueTask RunRegistrationCompanyMainAsync(CancellationToken cancellationToken)
{
//some await Tasks
}
private async ValueTask RunRegistrationCompanyMainAsync(string tenantId,CancellationToken cancellationToken)
{
//some await Tasks
}
所以我只能用一个参数调用 RunRegistrationCompanyMainAsync(CancellationTokenCancellationToken) 而不能用两个参数调用 RunRegistrationCompanyMainAsync(string tenantId,CancellationToken cancelationToken)
你能帮我传递字符串参数作为这个任务的参数吗?
解决方法
一段时间后,我找到了解决方案。 只需要像这样使用元组
public class CompanyRegistationQueue : ICompanyRegistationQueue
{
private readonly Channel<Tuple<CreateCompanyModel,Func<CreateCompanyModel,CancellationToken,ValueTask>>> _queue;
public CompanyRegistationQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity) { FullMode = BoundedChannelFullMode.Wait };
_queue = Channel.CreateBounded<Tuple<CreateCompanyModel,ValueTask>>**>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(Tuple<CreateCompanyModel,ValueTask>> workItem)
{
if (workItem == null) throw new ArgumentNullException(nameof(workItem));
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask<Tuple<CreateCompanyModel,ValueTask>>> DequeueAsync(CancellationToken cancellationToken)
{
var workItem = await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
然后这样调用
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(stoppingToken);
try
{
//item2 is task
await workItem.Item2(workItem.Item1,stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex,"Error occurred executing {WorkItem}.",nameof(workItem));
}
}
}
在代码中调用
var paramValue = new Tuple<CreateCompanyModel,ValueTask>>(createCompanyModel,RunRegistrationCompanyMainAsync);
await _companyRegistationQueue.QueueBackgroundWorkItemAsync(paramValue);
附言May by Tuple 不是最好的解决方案,而是它的工作
,在 QueueBackgroundWorkItemAsync(RunRegistrationCompanyMainAsync)
调用中,编译器实际执行 cast from method group into a delegate。但是要提供 Func
委托的实例,您不仅限于方法组,您可以提供 lambda expression 例如:
var someTenantId = ....
.....
_companyRegistationQueue.QueueBackgroundWorkItemAsync(ct => RunRegistrationCompanyMainAsync(someTenantId,ct));