尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁
(最终采用的是方法4)
问题详情见:.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长
看看在 Linux 与 Windows 上发生线程死锁的后果。
Linux:
Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -24 EMFILE too many open files
Windows(1.3万个线程):

引发问题的代码:
Task<IPAddress[]> task = System.Net.Dns.GetHostAddressesAsync(host);
task.Wait();
var addresses = task.Result;
上面的代码是在构造函数中调用的,只能同步调用,无法异步调用。
踩坑的条件:在一定数量的请求并发时才出现,如果只有很少的请求不会出现。所以,当我们发布时,将服务器从负载均衡上摘下来,结束进程,更新程序,在本机访问后(host解析已完成)挂上负载均衡,问题不会出现。如果不从负载均衡上摘下来,直接结束 asp.net core 程序的进程,新启动的进程就会出现这个问题。
接下来尝试解决方法。
1)参考 Synchronously waiting for an async operation, and why does Wait() freeze the program here ,将上面的代码改为:
var task = Task.Run(async () => { return await System.Net.Dns.GetHostAddressesAsync(host); });
task.Wait();
var addresses = task.Result;
死锁问题依旧。
2)参考 System.Data.SqlClient 中的实现:
private static async Task<Socket> ConnectAsync(string serverName, int port)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(serverName, port).ConfigureAwait(false);
return socket;
} // On unix we can't use the instance Socket methods that take multiple endpoints IPAddress[] addresses = await Dns.GetHostAddressesAsync(serverName).ConfigureAwait(false);
return await ConnectAsync(addresses, port).ConfigureAwait(false);
}
(注:SqlClient中在Windows上没有调用Dns.GetHostAddressesAsync)
将 Dns.GetHostAddressesAsync 放在一个 async/await 代理方法中:
private static async Task<IPAddress[]> GetHostAddressesAsyncProxy(string host)
{
return await System.Net.Dns.GetHostAddressesAsync(host);
}
死锁依旧。
3)修改 System.Net.Dns 的源代码,将异步方法
public static Task<IPAddress[]> GetHostAddressesAsync(string hostNameOrAddress)
{
NameResolutionPal.EnsureSocketsAreInitialized();
return Task<IPAddress[]>.Factory.FromAsync(
(arg, requestCallback, stateObject) => BeginGetHostAddresses(arg, requestCallback, stateObject),
asyncResult => EndGetHostAddresses(asyncResult),
hostNameOrAddress,
null);
}
改为同步方法
public static Task<IPAddress[]> GetHostAddressesAsync(string hostNameOrAddress)
{
NameResolutionPal.EnsureSocketsAreInitialized();
return Task.FromResult<IPAddress[]>(GetHostEntry(hostNameOrAddress).AddressList);
}
问题解决!
说明死锁问题的确是由于在构造函数中同步调用异步方法引起的。目前 System.Net.NameResolution 只提供了异步的 API 进行主机名的解析,上面的 GetHostEntry() 是同步方法,但只支持 netstandard2.0 ,目前 nuget.org 上的 System.Net.NameResolution 只支持到 netstandard 1.3 。
[备注]
---------------
修改 System.Net.Dns 的源代码,生成程序集(System.Net.NameResolution)并更新至 asp.net core 程序中的方法:
1)在github上签出corefx的源代码
2)修改 System.Net.Dns 的源代码
3)运行corefx文件夹中的init-tools.cmd命令
4)运行 MSBuild Command Prompt for VS2015 命令行,进入 corefx\src\System.Net.NameResolution\src 目录,运行 msbuild System.Net.NameResolution.builds 命令,会在 corefx\bin\Windows_NT.AnyCPU.Debug\System.Net.NameResolution\netcore50 文件夹中生成对应的程序集 System.Net.NameResolution.dll 。
5)将上一步生成的 System.Net.NameResolution.dll 复制到 asp.net core 站点的文件夹替换已有的同名文件即可。
---------------
4)尝试不修改 System.Net.Dns 的源代码进行解决
同步的 System.Net.Dns.GetHostEntry(string hostNameOrAddress) 方法可以解决问题,但它是为 netstandard2.0 api 实现的,在基于 netstandard1.6 的程序中无法直接调用,编译不通过。实际的 System.Net.NameResolution.dll 程序集中已经包含了 GetHostEntry() 实现,虽然编译时不让调用,但我们可以在运行时调用,那运行时如何调用呢?“反射”闪亮登场,用反射改为下面的代码:
var method = typeof(System.Net.Dns).GetMethod("GetHostEntry", BindingFlags.Public | BindingFlags.Static);
var addresses = ((IPHostEntry)method.Invoke(null, new object[] { host })).AddressList;
但发现 NuGet 服务器上发布的 System.Net.NameResolution 4.3.0 中并不包含 GetHostEntry() 这个方法。后来找到了另外一个私有静态方法 —— InternalGetHostByName() 。再后来发现 System.Net.DnsEndPoint ,使用它就不需要自己进行主机名的解析,但目前只支持 Windows 。
于是最终采取的方法是:Windows 平台用 DnsEndPoint ,非 Windows 平台用反射调用 System.Net.Dns.InternalGetHostByName() 方法。示例代码如下:
private void ConnectWithTimeout(Socket socket, EndPoint endpoint, int timeout)
{
if (endpoint is DnsEndPoint && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
IPAddress[] addresses;
var dnsEndPoint = ((DnsEndPoint)endpoint);
var host = dnsEndPoint.Host;
var method = typeof(System.Net.Dns).GetTypeInfo()
.GetMethod("InternalGetHostByName", BindingFlags.NonPublic | BindingFlags.Static);
if (method != null)
{
addresses = ((IPHostEntry)method.Invoke(null, new object[] { host, false })).AddressList;
}
else
{
Task<IPAddress[]> task = Dns.GetHostAddressesAsync(host);
task.Wait(timeout);
addresses = task.Result;
} var address = addresses.FirstOrDefault(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
if (address == null)
{
throw new ArgumentException(String.Format("Could not resolve host '{0}'.", host));
}
endpoint = new IPEndPoint(address, dnsEndPoint.Port);
} var completed = new AutoResetEvent(false);
var args = new SocketAsyncEventArgs();
args.RemoteEndPoint = endpoint;
args.Completed += OnConnectCompleted;
args.UserToken = completed;
socket.ConnectAsync(args);
if (!completed.WaitOne(timeout) || !socket.Connected)
{
using (socket)
{
throw new TimeoutException("Could not connect to " + endpoint);
}
}
}
相关链接:
尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁的更多相关文章
- C++构造函数中不能调用虚函数
在构造函数中调用虚函数,并不会产生多态的效果,就跟普通函数一样. c++ primer 第四版中497页15.4.5构造函数和析构中的虚函数讲到,如果在构造函数或析构函数中调用虚函数,则运行的是为构造 ...
- 解决在构造函数中使用Session,Session为null的问题
问题描述: public abstract class PageBase : System.Web.UI.Page 在PageBase中如何使用Session??? 我直接用 Session[&quo ...
- 关于父类私有属性在子类构造函数中super调用的解释
package test; public class Car { private int carMoney; //汽车租金 private String carName; //汽车名字 private ...
- 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题
在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...
- .NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长
一个 asp.net core 站点,之前运行在Linux 服务器上,运行一段时间后有时站点会挂掉,在日志中记录很多“EMFILE too many open files”的错误: Microsoft ...
- win2008加入域控之尝试解析加入域中域控制器的dns名称失败解决办法
记录下今天遇到以前没遇到的问题 加入域的时候提示“尝试解析加入域中控制器的DNS”名称失败 可能的原因: 如果确认dns没问题 dc正常访问,那可能就是因为域控制器无法向dns注册srv记录. SRV ...
- C#中的BackgroundWorker控件+Delegate.Invoke (委托同步调用)
C#中的BackgroundWorker控件+Delegate.Invoke (委托同步调用) 简单代码,记录一下.一个BackgroundWorker控件 backgroundWorkerRefr ...
- java中父类与子类, 不同的两个类中的因为构造函数由于递归调用导致栈溢出问题
/* 对于类中对成员变量的初始化和代码块中的代码全部都挪到了构造函数中, 并且是按照java源文件的初始化顺序依次对成员变量进行初始化的,而原构造函数中的代码则移到了构造函数的最后执行 */ impo ...
- 关于在C#中构造函数中调用虚函数的问题
在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...
随机推荐
- Aspx页面模拟WebService功能
在后台引入 using System.Web.Services 命名空间 然后在编写web服务方法: [WebMethod] public static string GetData(string t ...
- Beginning Scala study note(7) Trait
A trait provides code reusability in Scala by encapsulating method and state and then offing possibi ...
- 【Hibernate框架】flush机制
背景: 一个偶然的机会,我做了一个例子,中间我遇到了一个有意思的问题,就是在执行commit方法之前,做了两次save操作,如下: SessionFactory sf = new Configurat ...
- Python 学习第十六天 html 前端内容总结
一,css知识总结 1, css属性 css的属性包括以下内容 position:规定元素的定位类型 background:属性在一个声明中设置所有的背景属性 可以设置的如下属性: (1)back ...
- 【加精】手机话费充值API接口(PHP版)
电商周年庆,公司搞了一个关注微信公众号送小额话费的活动,送1元.2元.5元.10元.20元.50元等不同面值的. 为了实现话费充值服务,找了多家开通了话费接口服务,接入后测试.先是做接口整合的平台,P ...
- servlet中session的使用
1.获取session HttpSession session=request.getSession(); session.setAttribute("variety", vari ...
- jsoup获取文档类示例
import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsou ...
- bzoj3680模拟退火
看题意就是一道数学物理题,带权费马点 --这怎么是数学了,这也是物理的 所以要用物理方法,比如FFF 国际著名oi选手miaom曾说 模拟退火初温可以低,但是最好烧个几千次 国际著名物理课代表+1 ...
- Android应用-听听
听听是一款记歌词音乐播放器android应用. 功能特点:1.搜索网络歌词.2.本地音乐分类播放.3.离线保存网络歌词.4.页面整洁干净,风格清新. 下载APP 屏幕截图:
- #研发解决方案#分布式并行计算调度和管理系统Summoner
郑昀 创建于2015/11/10 最后更新于2015/11/12 关键词:佣金计算.定时任务.数据抽取.数据清洗.数据计算.Java.Redis.MySQL.Zookeeper.azkaban2.oo ...