Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't calling Task<T>.Result deadlock?

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))?

like image 996
James Ko Avatar asked Jul 06 '15 23:07

James Ko


People also ask

What happens if task is not awaited?

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.

Why use await Task run?

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.

What is the use of Task run in c#?

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.


1 Answers

Task.Result will only block in the presence of certain SynchronizationContexts. 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.

like image 78
i3arnon Avatar answered Sep 22 '22 12:09

i3arnon