2

I need to implement a third-party interface that doesn't support async, specifically IValueResolver from automapper.

I'm wondering what's the difference between these two pieces of code? Is there any advantage of using the first instead of the second? I will call an external async API on MethodAsync()

Will both lock the thread or just the second?

1

var myValue = Task.Run(async () => await MethodAsync()).Result;

2

var myValue = MethodAsync().Result;

2 Answers2

0

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.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • Actually I decompiled the API, the method I will call has just one line await xxxx, like a proxy just calling another method, is there any advantage using the first approach? –  Oct 08 '19 at 03:01
  • The other method may still have a synchronous part. And after that may await a third async method, which also has a synchronous part, etc. You must decompile all the way down to know exactly what's going on. – Theodor Zoulias Oct 08 '19 at 03:13
  • So I didn't understand your explanation, `The synchronous part is everything before the first await` –  Oct 08 '19 at 03:17
  • You are right. The synchronous part is more than that, since it also includes the synchronous part of the awaited async method, plus the synchronous part of the inner awaited async method inside the outer awaited async method, and so on until the deepest async method. – Theodor Zoulias Oct 08 '19 at 03:26
  • I updated my answer to clarify why the first approach is safer. – Theodor Zoulias Oct 08 '19 at 11:25
  • Thank you very much, it's more clear now, I think, as far I understood, the synchronous part is everything don't awaitable by the first async method called until the deepest part of that. Also, I did some research and tests and it seems that there is no difference between `Task.Run(async() => await MethodAsync()) and Task.Run(() => MethodAsync())` Do you agree with that? –  Oct 08 '19 at 14:18
  • Yes, these two are practically identical. – Theodor Zoulias Oct 08 '19 at 14:44
  • 1
    I update the answer with some examples, hoping to make clearer the distinction between synchronous and asynchronous parts of a method. – Theodor Zoulias Oct 08 '19 at 15:25
  • Thank you very much, I think you should edit the explanation about synchronous part of the method, as far as I understood the sync part is all synch code until the last call started by the MethodAsync() in the example, regarding use async for the call inside the task.run, I guess it's absolutely unnecessary, just Task.Run it ensures we will have the async method running in a separated thread on the thread pool –  Oct 09 '19 at 02:11
-1

Depending on how the method is implemented it is very possible that you will lock your thread on the second implementation.

If you want to be safe use the first implementation as you are guaranteed a new thread that will handle the async function, however this will cost you some scalability as you will be using 2 threads instead of one.

CVarg
  • 69
  • 10