升级Mysql.Data至8.*后Dapper QueryAsync调用永久挂起问题
问题分析与解决方案
问题根源
这是同步阻塞异步代码导致的死锁,MySQL 8.x版本的MySql.Data驱动对异步操作的上下文依赖更严格,直接触发了这个隐藏问题:
- 异步方法
QueryAsync默认会捕获当前线程的同步上下文(比如ASP.NET的SynchronizationContext) - 调用方用
.GetAwaiter().GetResult()同步阻塞时,会占用这个上下文不放 - 异步方法执行完成后需要回到原上下文处理结果,但上下文已被阻塞,最终形成互相等待的死锁
核心解决方案:全程异步(推荐)
不要在同步方法里阻塞调用异步方法,将调用方也改为异步实现:
1. 修改调用方法为异步
private async Task<bool> IsGapLiveSyncEnabled(Guid clientId) { return await _clientsService.IsLiveResultsEnabled(clientId); }
2. 优化原异步方法(可选,更规范)
将同步的connection.Open()改为异步的OpenAsync,并全程添加ConfigureAwait(false)避免捕获不必要的上下文:
public async Task<bool> IsLiveResultsEnabled(Guid clientId) { using (var connection = new MySqlConnection(_connectionString)) { await connection.OpenAsync().ConfigureAwait(false); var result = await connection.QueryAsync<bool>( @"SELECT someField FROM someTable WHERE Id = @clientId", new { clientId }) .ConfigureAwait(false); return result.FirstOrDefault(); } }
临时替代方案(无法修改调用方时)
如果受限于老代码必须同步调用,可以将异步操作放到线程池线程执行,避免捕获原上下文:
private bool IsGapLiveSyncEnabled(Guid clientId) { return Task.Run(() => _clientsService.IsLiveResultsEnabled(clientId)).GetAwaiter().GetResult(); }
注意:此方案仅作为临时妥协,长期来看建议逐步迁移为全程异步模式,彻底避免死锁和性能损耗。
内容的提问来源于stack exchange,提问作者kosnkov
相关产品推荐
相关产品推荐

