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)
)?
If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.
As you probably recall, await captures information about the current thread when used with Task. Run . It does that so execution can continue on the original thread when it is done processing on the other thread.
The Run method allows you to create and execute a task in a single method call and is a simpler alternative to the StartNew method. It creates a task with the following default values: Its cancellation token is CancellationToken.
Task.Result
will only block in the presence of certain SynchronizationContext
s. In console apps there isn't one so continuations are scheduled on the ThreadPool
. Just like they are when you use ConfigureAwait(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 use ConfigureAwait(false)
.
If you want to you can use Stephen Cleary's AsyncContext
that adds an appropriate SynchronizationContext
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 the async
and await
keywords. They simply return a task. Usually a wrapper over Task.Factory.FromAsync
. So it's probably "safe" blocking them anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With