1)
var myValue = Task.Run(async () => await MethodAsync()).Result;
The synchronous part of the asynchronous method MethodAsync
will run in a thread-pool thread.
2)
var myValue = MethodAsync().Result;
The synchronous part of the asynchronous method MethodAsync
will run in the caller's thread.
Now you may ask, what is the synchronous part of the asynchronous method?
The synchronous part is everything before the first await
inside the asynchronous method.
More precisely: The synchronous part is everything before the first await
of a non-completed awaitable.
Usually the synchronous part is minuscule, but when we talk about an unknown external API we cannot be 100% sure.
The difference between running blocking code in the caller's thread or in a thread-pool thread is probably not that important. In both cases the caller's thread will be blocked for the whole duration of the async call. Does the first approach (Task.Run
) offers any advantage? Usually the Task.Run
is added to solve problems of deadlocks, that can easily occur when await
and Wait/Result
are mixed. In your case such problems could occur if you use await
internally for some reason, or the external API uses await
internally without ConfigureAwait(false)
. In that case you'll notice it immediately and you'll probably fix it. So the advantage of using Task.Run
proactively is peace of mind. The disadvantage is that a thread-pool thread is utilized for running the synchronous part of the method. In most cases this part is very small, measured in μsec, so you shouldn't feel guilty if you follow the easy path.
Update: Here is an example of the first approach, that also demonstrates the synchronous and the asynchronous part of the external method:
private void Button1_Click(object sender, EventArgs e)
{
this.Text = YourMethod();
}
public static int YourMethod()
{
return Task.Run(async () => await ExternalMethodAsync()).Result;
}
public static async Task<string> ExternalMethodAsync()
{
Thread.Sleep(500); // Synchronous part
await Task.Delay(500).ConfigureAwait(false); // Asynchronous part
return $"Time: {DateTime.Now:HH:mm:ss.fff}";
}
In this case the prophilactic use of Task.Run
is redundant because the external library follows the good practice of awaiting with ConfigureAwait(false)
.
Here is an example of the second approach:
public static int YourMethod()
{
return ExternalMethodAsync().Result;
}
public static async Task<string> ExternalMethodAsync()
{
Thread.Sleep(500); // Synchronous part
await Task.Delay(500); // Asynchronous part
return $"Time: {DateTime.Now:HH:mm:ss.fff}";
}
This code deadlocks. Even a single non-configured top-level await
inside the external library will cause a deadlock, if you request the Result
directly, without Task.Run
.