繁体   English   中英

等待后 EF Core 异步方法挂起/死锁

[英]EF Core async methods hang/deadlock after await

更新:这个问题实际上与异步无关——我的数据库不同步并且 LINQ 查询失败。 DSharpPlus 丢弃捕获的异常,因此没有任何问题的迹象。 有关更多详细信息,请参阅下面的我的自我回答。

I am encountering a hang/deadlock while attempting to use the async methods of EF Core on .NET 5. This is in a console app (so no ASP.NET, WPF, or anything like that) but I am using the Microsoft.Extensions.Hosting package管理应用程序生命周期。 我还使用了第 3 方中间件DSharpPlus ,它负责将工作分派到线程池中,这是我调用 EF 的地方。 依赖注入正在使用中,通过 Host.CreateDefaultBuilder 配置。

DSharpPlus 从 IHostedService 异步运行(通过作用域 DI 包装器 - 因为托管服务必须是单例并且 DbContext 是作用域的)。 在从新的 DI scope 创建处理程序代码的新实例后,它将事件分派到线程池(通过 Task.Run)。 处理程序代码调用注入服务的新创建实例,该实例最终调用 DbContext(也是一个新实例)。

这比我想的要复杂,但是我读过的所有内容都表明它应该可以工作。 不幸的是,当我尝试使用 EF 时它会中断。 任何使用 EF 的代码在等待时都会挂起,但是当 EF 被存根时,相同的代码仍然有效。 这是最短/最简单的事务(为了便于阅读而减少了一点):

// This line is from DSharpPlus (not my code) but I'm including it for clarity.
// This line is called at end of a network event handler and exists only to dispatch the event to a thread pool (it immediately returns after this line)
// ExecuteCommandAsync is responsible for creating a new DI scope and fresh instance of the command handler, which contains the function below.
_ = Task.Run(async () => await this.ExecuteCommandAsync(ctx).ConfigureAwait(false));

// Called eventually by above code, inside a fresh object with a new DI scope
// _hydrationLeaderboard, _hydrationOptions, and _logger are injected each time.
// _logger is a standard .NET ILogger instance.
public async Task ScoresCommand(CommandContext ctx)
{
    using (_logger.BeginScope($"CmdScores.ScoresCommand@{ctx.Message.Id.ToString()}"))
    {
        _logger.LogDebug("Requested by [{user}]", ctx.User);
        
        // This never returns
        var leaderboard = await _hydrationLeaderboard.GetLeaderboard(_hydrationOptions.LeaderboardSize);
    
        // ** snipped **
    }
}

// Called by above code
// _selfcareDb is new DbContext instance from DI
public Task<List<HydrationLeaderboardEntry>> GetLeaderboard(int top = 3)
{
    // This never returns. The original has additional LINQ, but it still fails like this too.
    // It also fails if method is async/await
    return _selfcareDb.UserScores
        .Select((us, idx) => new HydrationLeaderboardEntry(
            // **Snip**, just copying properties from DB entity to service entity
        )
        .ToListAsync();
}

我想我已经排除了异步挂起的常见嫌疑人——我没有使用.Wait、.GetResult 或任何其他类似的调用。 我没有混入任何同步代码(除非我错过了)。 我确保为每个任务注入一个唯一的 DbContext 实例。 作为测试,我删除了所有 EF 代码并将其替换为内存字典。 这工作得很好。 我什至查看了 DSharpPlus 的代码,并没有发现任何可疑之处。

如果我在某处犯了明显的错误,请多多道歉! 这是我第一个使用 async EF 的项目,而且我对 async/await 通常还是比较陌生。 如果有帮助的话,我很乐意分享这个项目的完整代码。

提前感谢您的任何帮助!

编辑:我忘了提,我使用的数据库是 Sqlite。

更新: Task.Run 调用似乎不是问题。 即使使用在单个线程中完全使用异步/等待的备用事件调度,对 EF 的调用仍然挂起。

您应该删除.ConfigureAwait(false) ,ConfigureAwait 带有 false 表示您没有等待任务完成,我真的建议您阅读此博客

这是一个数据库错误,我搞砸了我的 EF 迁移,所以数据库是空的。 这显然会引发异常。 在此过程中的某个地方,异常被吞并并隐藏起来。 我修复了数据库错误,现在一切正常。

我还在每个命令处理程序中添加了一个简单的 try/catch 块,现在只要出现错误,我就会得到 output。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM