C# 为什么我的后台工作程序在使用Task.Delay时会死锁?
在我的项目中,我使用ASP.NET Core 3.1和托管服务后台工作程序C# 为什么我的后台工作程序在使用Task.Delay时会死锁?,c#,asp.net-core,.net-core,C#,Asp.net Core,.net Core,在我的项目中,我使用ASP.NET Core 3.1和托管服务后台工作程序 protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { var result = await _messageBus.Get(); if (result != null)
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var result = await _messageBus.Get();
if (result != null)
{
await _dbContext.UpdateData(result, stoppingToken);
}
await Task.Delay(5000, stoppingToken);
}
}
在DbContext中,我执行一些逻辑,然后调用await命令.ExecuteNonQueryAsync(stoppingToken)代码>。在那条线上,工人死锁了
await using var connection = new SqlConnection(dbConnection);
await connection.OpenAsync(stoppingToken);
var command = connection.CreateCommand();
command.CommandText = query;
await command.ExecuteNonQueryAsync(stoppingToken);
然后,我将后台工作人员更改为:
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_timer = new Timer(async state => await Run(state, stoppingToken), null,
TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private async Task Run(object state, CancellationToken stoppingToken)
{
var result = await _messageBus.Get();
if (result != null) await _dbContext.UpdateData(result, stoppingToken);
}
这最终奏效,避免了僵局。然而,我真的不知道为什么这样做有效,避免了僵局。是什么使计时器类与仅使用任务延迟类不同?我强烈怀疑您的代码实际上不是异步的,这将导致启动问题(可能看起来像死锁)。它没有很好的文档记录,但是ExecuteAsync
必须是异步的。因此,如果您在该方法的开头有阻塞代码(例如,如果“从消息总线获取消息”代码实际上是同步阻塞的,直到收到消息),那么您需要将其包装在任务中。运行:
protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(async () =>
{
while (!stoppingToken.IsCancellationRequested)
{
var result = await _messageBus.Get();
if (result != null)
{
await _dbContext.UpdateData(result, stoppingToken);
}
await Task.Delay(5000, stoppingToken);
}
});
在我自己的代码中,我使用一个单独的基本类型来执行任务。运行,这样它就不那么难看了。您如何调用ExecuteAsync
您是否调用过ExecuteAsync
?或者是某个框架在调用它?您发布的所有代码都不会被阻止,因此不会导致死锁。您仍然没有显示调用ExecuteAsync
的位置。您如何确定它是死锁的?这在没有同步上下文的.NET Core中不容易做到。我的错误是,ExecuteAsync
来自BackgroundService
,ASP.NET Core在向服务集合添加托管服务时调用该服务。要查看调用ExecuteAsync
的位置,请参见此处:您可以在顶部添加wait Task.Yield()
,以确保它不会阻塞。我发现正在使用的用户没有写入权限。当wait command.executenonquerysync(stoppingToken)时调用代码>时引发异常。但我还是很困惑,为什么该方法不将其任务返回为异常失败?相反,它只是停止并挂起。@TravisBoatman:这种行为取决于您的数据库提供程序。如果权限不足,我当然希望ExecuteNonQueryAsync
抛出。“你确定它不会扔吗?”斯蒂芬克利里我发现了它为什么会这样。后台工作程序ExecuteAync
方法的行为与处理异常时人们可能认为的不同。它没有很好的文档记录,仍在讨论中。有关更多信息,请参见此处。--哦,对了。我以为你的意思是它实际上在ExecuteNonQueryAsync
上死锁了。如果从ExecuteAsync
引发异常,则该异常不会按设计中断整个流程。如果希望在ExecuteAsync
方法引发异常(或在没有异常的情况下退出)时退出该进程,则需要使用IHostApplicationLifetime
自行编写代码。