又踩.NET Core的坑:在同步方法中调用异步方法Wait时发生死锁(deadlock)
来源:互联网 发布:python 查看环境变量 编辑:程序博客网 时间:2024/06/06 01:06
之前在将 Memcached 客户端 EnyimMemcached 迁移 .NET Core 时被这个“坑”坑的刻骨铭心(详见以下链接),当时以为只是在构造函数中调用异步方法(注:这里的异步方法都是指基于Task的)才会出线死锁(deadlock)问题。
解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题
在同步方法中调用异步方法时如何避免死锁问题
.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长
尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁
最近在使用 redis 客户端 StackExchange.Redis 时也遇到了这个问题, 详见 ASP.NET Core中StackExchange.Redis连接redis服务器的问题 。
StackExchange.Redis 中死锁问题发生在下面的代码:
private static ConnectionMultiplexer ConnectImpl(Func<ConnectionMultiplexer> multiplexerFactory, TextWriter log)
{
IDisposable killMe = null;
try
{
var muxer = multiplexerFactory();
killMe = muxer;
// note that task has timeouts internally, so it might take *just over* the regular timeout
var task = muxer.ReconfigureAsync(true, false, log, null, "connect");
if (!task.Wait(muxer.SyncConnectTimeout(true)))
{
task.ObserveErrors();
if (muxer.RawConfig.AbortOnConnectFail)
{
throw ExceptionFactory.UnableToConnect("Timeout");
}
}
if (!task.Result) throw ExceptionFactory.UnableToConnect(muxer.failureMessage);
killMe = null;
return muxer;
}
finally
{
if (killMe != null) try { killMe.Dispose(); } catch { }
}
}
ConnectImpl() 是一个同步方法,muxer.ReconfigureAsync() 是一个 async 异步方法。在 Linux 上运行时, task.Wait(muxer.SyncConnectTimeout(true)) 会因为等待超时而返回 false ,从而出现下面的错误:
StackExchange.Redis.RedisConnectionException: It was not possible to connect to the redis server(s); to create a disconnected multiplexer, disable AbortOnConnectFail. Timeout at StackExchange.Redis.ConnectionMultiplexer.ConnectImpl(Func`1 multiplexerFactory, TextWriter log)
如果改为 task.Wait() ,在 ASP.NET Core 程序中调用时,请求会因为死锁而卡死。
这是一个典型的同步方法调用异步方法在 Wait 时的死锁问题(详见 Don't Block on Async Code),通常的解决方法是使用 .ConfigureAwait(false); (异步任务执行完成时不获取SynchronizationContext)。StackExchange.Redis 的开发者当然知道这一点,在 muxer.ReconfigureAsync() 中调用每一个异步方法时都加上了 .ConfigureAwait(false); 。但我们遇到的实际情况显示,这一招在 .NET Framework 中管用,在 .NET Core 中却不管用,这可能与 .NET Core 在异步机制上的改变有关,比如在异步方法中 System.Threading.SynchronizationContext.Current 的值总是为 null ,详见 ASP.NET Core 1.0 SynchronizationContext 。
这个问题在 Liunx 上很容易出现,而在 Windows 上需要一定的并发请求才会出现。
后来在 Microsoft.AspNetCore.DataProtection.AzureStorage 中也发现了在同步方法中调用异步方法的代码:
public IReadOnlyCollection<XElement> GetAllElements()
{
var blobRef = CreateFreshBlobRef();
// Shunt the work onto a ThreadPool thread so that it's independent of any
// existing sync context or other potentially deadlock-causing items.
var elements = Task.Run(() => GetAllElementsAsync(blobRef)).GetAwaiter().GetResult();
return new ReadOnlyCollection<XElement>(elements);
}
参考上面的代码,在 StackExchange.Redis 中的 ConnectImpl() 方法中改为 Task.Run() 调用异步方法,死锁问题依旧。
原文地址:http://www.cnblogs.com/dudu/p/6251266.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
- 又踩.NET Core的坑:在同步方法中调用异步方法Wait时发生死锁(deadlock)
- 调试死锁(deadlock)的方法
- 在Asp.net中调用异步方法-使用信号量
- 在Asp.net中调用异步方法-使用信号量
- java 为什么wait(),notify(),notifyAll()必须在同步方法/代码块中调用?
- java 为什么wait(),notify(),notifyAll()必须在同步方法/代码块中调用?
- std::thread 在DLLMain 中会发生死锁 std::thread cause deadlock in DLLMain
- 如何在ORACLE中异步调用存储过程的方法
- .NET异步方法调用的例子
- Mysql 死锁发生时的详细分析方法
- C#的新特性体验(异步方法的同步调用)
- 为什么wait(),notify()和notifyAll()必须在同步块或同步方法中调
- 调用wait的语句一定要写在synchronized 方法中吗?
- 使用 Task.Wait()?立刻死锁(deadlock)
- 使用异步方式调用同步方法
- 使用异步方式调用同步方法
- 使用异步方式调用同步方法
- 使用异步方式调用同步方法
- Fabio 安装和简单使用
- 微软开源Visual Studio测试平台VSTest
- Flux --> Redux --> Redux React 入门 基础实例教程
- 在ASP.NET Core Web API上使用Swagger提供API文档
- 分布式系统搭建:服务发现揭秘
- 又踩.NET Core的坑:在同步方法中调用异步方法Wait时发生死锁(deadlock)
- 浅谈开发模式及架构发展
- 开箱即用
- Swagger+AutoRest 生成web api客户端(.Net)
- 异步广度优先搜索算法
- [Asp.Net Core轻量级Aop解决方案]AspectCore Project 介绍
- 如何优雅的使用RabbitMQ
- Visual Studio现可使用EditorConfig
- 互联网背景下知识半衰期这么短,如何学习?