获取主机条目异步冻结

本文关键字:异步 冻结 主机 获取 | 更新日期: 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的规范用例,导致代码平平的,易于遵循的,死锁的可能性很小。