Reputation: 2619
I have a problem that occurs when i have deployed the code to the production server. It does not happen locally on my computer. When i run the line
var host = await Dns.GetHostEntryAsync(adresse);
The production environment freezes or awaits indefinitely. Why does this happen?
Here is the code:
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;
}
}
UPDATE: The async method is called like this from a sync method that is doing the same thing (could this be the culprit).
public string Hent(string ip)
{
return HentAsync(ip).Result;
}
Upvotes: 1
Views: 2606
Reputation: 2669
The most likely cause is that the origin of this call is running on the UI thread; perhaps the call to Hent()
updates a UI element?
The problem is well documented here: await works but calling task.Result hangs/deadlocks
Hent()
calls HentAsync()
and then await Dns.GetHostEntryAsync()
. This last call spawns a new thread, but wants to return to the calling thread when it has finished, which in this case is the UI thread, but it cannot do this because .Result
is squatting on the thread rather than yielding it temporarily like it would have with await
.
As @spender and @Dirk point out, one solution is to add .ConfigureAwait(false)
to await Dns.GetHostEntryAsync()
. In fact you should add this to every await
where you don't care what thread context the function continues in.
This approach is still quite fragile though, as any time you forget to do it or execution takes a path with an unmodified await
, the problem will happen again. The best option is to make the top-level UI-threaded function async
and then await
every function call that could cause a delay. This is the canonical use-case for async / await and leads to flat, easy to follow code with very little chance of deadlock.
Upvotes: 2