Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么我的后台工作程序在使用Task.Delay时会死锁?_C#_Asp.net Core_.net Core - Fatal编程技术网

C# 为什么我的后台工作程序在使用Task.Delay时会死锁?

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)

在我的项目中,我使用ASP.NET Core 3.1和托管服务后台工作程序

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
自行编写代码。