.NET 8分布式事务调用Dispose时发生死锁,寻求解决方法
问题分析与解决方案
问题根源
你遇到的TransactionScope.Dispose()死锁,核心原因在于显式调用SqlConnection.Close()提前释放了连接:
- 手动关闭连接时,连接会被放回连接池,但此时分布式事务的提交逻辑还未完成(
ts.Complete()仅标记事务需要提交,实际提交操作在Dispose()阶段执行)。 - 事务管理器在
Dispose()时需要和数据库交互完成分布式事务的提交,但提前释放的连接可能导致事务资源无法正确关联,或连接池中的连接被其他操作占用,进而引发死锁。 - 注释
ts.Complete()后,事务会直接回滚,Dispose()阶段无需执行复杂的提交逻辑,因此不会触发死锁。
修复方案
1. 移除显式Close调用,改用using块自动管理资源
SqlConnection和TransactionScope都实现了IDisposable,直接用using块包裹即可自动处理资源释放,无需手动调用Close()或Dispose()。修正后的代码如下:
TransactionManager.ImplicitDistributedTransactions = true; var transactionOptions = new TransactionOptions(); transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; transactionOptions.Timeout = TimeSpan.FromSeconds(3600); // 用using块包裹TransactionScope,自动处理Dispose using (IDbConnection connection1 = new SqlConnection(infocitiConnectionString)) using (IDbConnection connection2 = new SqlConnection(storageConnectionString)) using (var ts = new TransactionScope(TransactionScopeOption.Required, transactionOptions)) { connection1.Open(); //connection1.Execute(sql: sqlDelete); connection2.Open(); //connection2.Execute(sql: sqlInsert); ts.Complete(); }
这样可以确保连接在事务完成前保持登记状态,让事务管理器正确处理分布式事务的提交流程。
2. 确认连接字符串与分布式事务配置
- 检查SQL Server连接字符串是否包含
Enlist=true(默认值为true,若手动设置为false会导致事务无法登记到分布式事务)。 - 若涉及远程数据库,确保两台服务器的MSDTC服务已启用并配置正确(分布式事务依赖MSDTC进行跨资源协调)。
3. 排查数据库端锁资源
死锁也可能源于数据库端的资源冲突,可通过以下SQL查询当前锁状态:
-- 查看当前事务锁 SELECT * FROM sys.dm_tran_locks -- 查看阻塞进程 EXEC sp_who2
确认是否有其他未提交的事务占用了目标资源,导致提交时等待锁释放。
4. 优化事务内操作时长
尽量缩短事务执行时间,避免在事务内执行非必要的耗时操作,减少锁占用时间,降低死锁概率。
内容的提问来源于stack exchange,提问作者Maksim Perevoshchikov
相关产品推荐
相关产品推荐

