获取主机条目异步冻结
本文关键字:异步 冻结 主机 获取 | 更新日期: 2023-09-27 18:11:40
在将代码部署到生产服务器时出现了一个问题。它不会在我的计算机上本地发生。当我运行
这行var host = await Dns.GetHostEntryAsync(adresse);
生产环境冻结或无限期等待。为什么会发生这种情况?
代码如下:
public async Task<string> HentAsync(string ip)
{
if (string.IsNullOrWhiteSpace(ip) || kLoopbackIp.Contains(ip))
{
return string.Empty;
}
if (IpHostname.ContainsKey(ip))
{
UpdateAndRemoveOldElements(ip);
return IpHostname[ip].Item1;
}
try
{
IPAddress adresse = IPAddress.None;
if (!IPAddress.TryParse(ip, out adresse))
{
return string.Empty;
}
var host = await Dns.GetHostEntryAsync(adresse);
string hostname = host.HostName;
IpHostname.AddOrUpdate(ip,
new Tuple<string, DateTime>(hostname, DateTime.Now),
(x, y) => new Tuple<string, DateTime>(hostname, DateTime.Now));
return hostname;
}
catch (Exception)
{
// Tar i mot exception og returnerer tom string
return string.Empty;
}
}
更新:异步方法是这样被一个同步方法调用的,这个同步方法也在做同样的事情(这可能是罪魁祸首)。
public string Hent(string ip)
{
return HentAsync(ip).Result;
}
最可能的原因是这个调用的起源是在UI线程上运行的;也许对Hent()
的调用更新了UI元素?
这个问题在这里有很好的记录:await工作但是调用task。结果挂起/死锁
Hent()
先呼叫HentAsync()
,再呼叫await Dns.GetHostEntryAsync()
。这个最后的调用产生一个新线程,但是当它完成时想要返回到调用线程,在这种情况下是UI线程,但是它不能这样做,因为.Result
占用了线程,而不是像await
那样暂时放弃它。
正如@spender和@Dirk指出的,一个解决方案是将.ConfigureAwait(false)
添加到await Dns.GetHostEntryAsync()
。事实上,你应该把它添加到每个await
中,在那里你不关心函数继续在哪个线程上下文中。
这种方法仍然是相当脆弱的,因为任何时候你忘记这样做,或者执行采用一个未修改的await
路径,问题将再次发生。最好的选择是将顶级ui线程函数设置为async
,然后将每个可能导致延迟的函数调用设置为await
。这是async/await的规范用例,导致代码平平的,易于遵循的,死锁的可能性很小。