After reading this post a few months ago, I became paranoid of getting the Result
of a Task<T>
and incessantly wrapped all of my calls to it with a ConfigureAwait(false)
or Task.Run
. However, for some reason the following code completes successfully:
public static void Main(string[] args)
{
var arrays = DownloadMany();
foreach (var array in arrays);
}
IEnumerable<byte[]> DownloadMany()
{
string[] links = { "http://google.com", "http://microsoft.com", "http://apple.com" };
using (var client = new HttpClient())
{
foreach (var uri in links)
{
Debug.WriteLine("Still here!");
yield return client.GetByteArrayAsync(uri).Result; // Why doesn't this deadlock?
}
}
}
The code prints Still here!
3 times and then exits. Is this specific to HttpClient
that it is safe to call Result
on (as in the people who wrote it have peppered it with ConfigureAwait(false)
)?
Task.Result
will only block in the presence of certainSynchronizationContext
s. In console apps there isn't one so continuations are scheduled on theThreadPool
. Just like they are when you useConfigureAwait(false)
.In UI threads for example there is one that schedules continuations to the single UI thread. If you wait synchronously with
Task.Result
using the UI thread, on a task that can only complete on the UI thread, you've got a deadlock.Moreover, a deadlock depends on the implementation of
GetByteArrayAsync
. You can only deadlock if it's an async method and its awaits don't useConfigureAwait(false)
.If you want to you can use Stephen Cleary's
AsyncContext
that adds an appropriateSynchronizationContext
to your console app to test whether your code can block in UI apps (or ASP.Net).About
HttpClient
's (and most of .NET's) Task-returning methods: they aren't technically async. They don't use theasync
andawait
keywords. They simply return a task. Usually a wrapper overTask.Factory.FromAsync
. So it's probably "safe" blocking them anyway.