(最终采用的是方法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()引起的线程死锁的更多相关文章

  1. C++构造函数中不能调用虚函数

    在构造函数中调用虚函数,并不会产生多态的效果,就跟普通函数一样. c++ primer 第四版中497页15.4.5构造函数和析构中的虚函数讲到,如果在构造函数或析构函数中调用虚函数,则运行的是为构造 ...

  2. 解决在构造函数中使用Session,Session为null的问题

    问题描述: public abstract class PageBase : System.Web.UI.Page 在PageBase中如何使用Session??? 我直接用 Session[&quo ...

  3. 关于父类私有属性在子类构造函数中super调用的解释

    package test; public class Car { private int carMoney; //汽车租金 private String carName; //汽车名字 private ...

  4. 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...

  5. .NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长

    一个 asp.net core 站点,之前运行在Linux 服务器上,运行一段时间后有时站点会挂掉,在日志中记录很多“EMFILE too many open files”的错误: Microsoft ...

  6. win2008加入域控之尝试解析加入域中域控制器的dns名称失败解决办法

    记录下今天遇到以前没遇到的问题 加入域的时候提示“尝试解析加入域中控制器的DNS”名称失败 可能的原因: 如果确认dns没问题 dc正常访问,那可能就是因为域控制器无法向dns注册srv记录. SRV ...

  7. C#中的BackgroundWorker控件+Delegate.Invoke (委托同步调用)

    C#中的BackgroundWorker控件+Delegate.Invoke (委托同步调用) 简单代码,记录一下.一个BackgroundWorker控件  backgroundWorkerRefr ...

  8. java中父类与子类, 不同的两个类中的因为构造函数由于递归调用导致栈溢出问题

    /* 对于类中对成员变量的初始化和代码块中的代码全部都挪到了构造函数中, 并且是按照java源文件的初始化顺序依次对成员变量进行初始化的,而原构造函数中的代码则移到了构造函数的最后执行 */ impo ...

  9. 关于在C#中构造函数中调用虚函数的问题

    在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...

随机推荐

  1. 在Mac上开启自带的Apache,httpd服务

    下面演示的是Mac自带的httpd服务 启动httpd服务 AppledeMacBook-Pro:python2_zh apple$ sudo apachectl start AppledeMacBo ...

  2. JAVA运行时问题诊断-工具应用篇

    该BLOG内容是之前在部门组织讨论运行时问题时自己写的PPT内容,内容以点带面,主要是方便以后自己回顾查看. 大纲包括:1.运行时问题分类 2.服务器自带工具 3.其他工具 4.例子 5.实际情况 运 ...

  3. 【Mybatis架构】 延迟加载

    在上一篇博客中,我们提到过有关于Mybatis输出映射中resultMap能够实现延迟加载的事,然而真的是所有的resultMap都能实现延迟加载还是咋地啊?现在我们就来对那一句话做一下阐述和实例说明 ...

  4. jquery lazyload延迟加载技术的实现原理分析

    懒加载技术(简称lazyload)并不是新技术,它是js程序员对网页性能优化的一种方案.lazyload的核心是按需加载.在大型网站中都有lazyload的身影,例如谷歌的图片搜索页,迅雷首页,淘宝网 ...

  5. css3选择器

    原网站 cnblogs.com/tianshang/p/5982513.html通配符选择器 通配选择器的作用就是对页面上所有的元素都生效, 页面上的所有标签都会展示出通配符选择器设定的样式. 这样的 ...

  6. 关于SVN出现 svn working copy locked的原因及解决方法

    今天使用SVN时电脑卡住了,于是结束进程中断了SVN的操作,于是出现了如题问题, 产生这种情况大多是因为上次svn更新命令执行失败且被自动锁定了. 如果cleanup没有效果的话只好手动删除锁定文件. ...

  7. 《DSP using MATLAB》示例Example5.23

    代码: conv_time = zeros(1,150); fft_time = zeros(1, 150); % % Nmax = 2048; for L = 1:150 tc = 0; tf = ...

  8. BZOJ3438 小M的作物(最小割)

    题目 Source http://www.lydsy.com/JudgeOnline/problem.php?id=3438 Description 小M在MC里开辟了两块巨大的耕地A和B(你可以认为 ...

  9. 【Oracle】dba_jobs字段说明

    dba_jobs 1 字段(列) 数据类型 描述 JOB NUMBER 任务的唯一标示号 LOG_USER ) 提交任务的用户 PRIV_USER ) 赋予任务权限的用户 SCHEMA_USER ) ...

  10. Python调用HTTP接口并传递cookie

    #get接口调用 import urllib import urllib2 get_url = "http://10.10.3.63/test?id=123&name=nba&quo ...