Reputation: 13
As we all know to access the Result property of Task in the UI thread and synchronous mode will deadlock.
As theoretical follow code will deadlock but not. Can you please explain why?
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
//MVC action
public ActionResult Index()
{
var result = System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result; // deadlock is expectation but not :(
...
}
I thought that System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result
is simialar to GetJsonAsync(...).Result
in some way, but not.
GetJsonAsync(...)).Result
will deadlock
but System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result
is not.
Upvotes: 1
Views: 747
Reputation: 456717
Result
doesn't cause a deadlock by itself. It causes a deadlock when called from a single-threaded context if there is an await
for that task that also needs that context.
await
by default captures a context and resumes on that context. (You can use ConfigureAwait(false)
to override this default behavior and resume on a thread pool thread instead.)Result
blocks the current thread until that Task
is complete. (You can use await
to consume a task asynchronously to avoid blocking a thread.)Task.Run
to run code on a thread pool thread, with a thread pool context, which is not a single-threaded context.)So, to get a deadlock, you need to have an await
that captures a single-threaded context, and then block a thread inside that context (e.g., calling Result
on that task). The await
needs the context to complete the Task
, but the context only allows one thread at a time, and the Result
is keeping a thread blocked in that context until the Task
completes.
In your example, you're calling GetJsonAsync
inside a Task.Run
, which runs it on the thread pool. So the await
in GetJsonAsync
(and the await
in the delegate passed to Task.Run
) capture the thread pool context, not the ASP.NET request thread context. Then your code calls Result
, which does block the ASP.NET request thread (and its context), but since the await
doesn't need that context, there's no deadlock.
Upvotes: 6