c# - 对于事件驱动的后台任务,从 ExecuteAsync 返回什么?
问题描述
作为我的 REST API 的一部分,我有一个 BackgroundService 订阅消息队列并等待接收特定消息并根据消息类型执行某些操作。
在 ExecuteAsync 中,我目前正在返回 Task.CompletedTask,因为我没有任何要“等待”的东西,但是我认为这实际上是不正确的,并且可能存在潜在问题,尽管我不确定究竟会出现什么问题或出现什么问题因此。
以无限延迟简单地“等待”Task.Delay 是否可以接受?就像是:
await Task.Delay(Timeout.infinite, cancellationToken);
解决方案
TLDR
本质上它归结为: Return Task.CompleteTask
。
我什至建议直接使用IHostedService
而不是BackgroundService
因为你并没有真正从我所看到的中获得任何东西。
托管服务
首先要研究的是IHostedService
。这个接口是BackgroundService
实现的,并且是在主机启动时允许运行代码的接口。接口只添加了StartAsync
andStopAsync
方法(没有ExecuteAsync
方法)。
如果我们查看源代码,Microsoft.Extensions.Hosting
我们可以看到,当我们调用它时AddHostedService
,它所做的就是将 IHostedService 添加到IServiceProvider
.
public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
return services;
}
然后,它里面Host.StartAsync
做了一些启动的东西,但主要的是它如何启动IHostedService
's.
public async Task StartAsync(CancellationToken cancellationToken = default)
{
// Omitted for brevity
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();
foreach (IHostedService hostedService in _hostedServices)
{
// Fire IHostedService.Start
await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
if (hostedService is BackgroundService backgroundService)
{
_ = HandleBackgroundException(backgroundService);
}
}
// Omitted for brevity
}
为什么要展示这一切?我认为重要的是要知道这里没有真正的魔法发生。每当StartAsync
调用主机时,它所做的只是调用StartAsync
IHostedServices。反之StopAsync
,IHostedService
仅在被调用时才Host.StopAsync
被调用。
那么BackgroundService 有什么特别之处呢?
后台服务
您可能已经注意到在开始IHostedService
' 检查它是否为BackgroundService
. 您已经发布了 的源链接BackgroundService
,但我将在此处发布主要部分以供查看。
public virtual Task ExecuteTask => _executeTask;
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Create linked token to allow cancelling executing task from provided token
_stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
// Store the task we're executing
_executeTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it, this will bubble cancellation and failure to the caller
if (_executeTask.IsCompleted)
{
return _executeTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
如您所见,它实际上从未等待从ExecuteAsync
. 这就是允许我们在BackgroundService
. 如果您尝试在Task.Delay
normal 中以巨大的延迟执行 a IHostedService
,您的 Host 将永远无法正常启动。
在您返回Task.CompleteTask
from的情况下ExecuteAsync
,它只会将完成的任务返回StartAsync
from Host
。
那么用BackgroundTask
's 做这一切有什么意义呢?如果我错了,有人纠正我,但从看起来它似乎只存在于长时间运行的任务上的异常处理。如果我们查看HandleBackgroundException
内部Host.StartAsync
,我们会看到:
private async Task HandleBackgroundException(BackgroundService backgroundService)
{
try
{
await backgroundService.ExecuteTask.ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.BackgroundServiceFaulted(ex);
}
}
它所做的一切都是从任务中获取ExecuteAsync
并等待它,这使我们能够看到通过将其发送到 Logger 所引发的任何异常。
结论
根据我在源代码中看到的内容,我没有看到任何关于您的案例需要BackgroundService
.
你说过'它不是一个异步 API,因此不需要等待',这对我来说意味着你没有从BackgroundService
.
您应该只使用 aIHostedService
并在 中设置回调StartAsync
,然后在 中删除它们StopAsync
。我没有理由看到这不起作用。这不像你IHostedService
被停止,因为它不再“运行”了。
如果您想坚持使用 BackgroundService,返回Task.CompleteTask
而不是等待延迟会更明智。延迟仍然会使用一些资源来保持计时器,即使它很小。
推荐阅读
- deep-learning - 在 Keras 2.0 中使用合并
- ios - 从 Xcode 导出 IPA 以使用另一个分发证书重新签名
- javascript - 为什么 json 可以轻松导入而 csv 在 Nodejs Typescript javascript 中无法导入?
- python - 无法在 python 3.7 中导入本地化库
- c# - 在 C# 中将文件从 FTP 直接传输到 Azure Blob 存储
- javascript - 为什么我的阶乘函数返回 NaN?
- webassembly - 如何在 webassembly 中编码图像,如 base64
- mysql - 时间格式问题:比较不起作用
- c# - 使用 TreeInstance 将树添加到地形 C#
- html - Bootsrap 轮播图像大小正在改变