What is the difference between the next delegate Func <Task <T>> async?

If I have the following method:

public async Task<T> DoSomethingAsync<T>(Func<Task<T>> action) { // bunch of async code..then "await action()" } 

What is the difference between the following two customs:

 public async Task MethodOneAsync() { return await DoSomethingAsync(async () => await SomeActionAsync()); } public async Task MethodTwoAsync() { return await DoSomethingAsync(() => SomeActionAsync()); } 

Both compile .. both work .. no R # warning ...

What is the difference (if any)? Will both methods run true async if they are expected by the caller?

+10
c # async-await
source share
3 answers

Short answer

MethodOneAsync() indeed Async and should be used, but MethodTwoAsync() not genuine Async because it calls a thread pool thread

Long answer

For testing and management, I simplified your code as follows:

Execution from the Linqpad Basic method as follows:

 var resultTask = MethodOneAsync(); // Comment one the methods resultTask.Result.Dump(); 

Actual code

 public async Task<int> DoSomethingAsync(Func<Task<int>> action) { return await Task.FromResult<int>(3); } public async Task<int> MethodOneAsync() { await Task.Delay(10); return await DoSomethingAsync(async () => await Task.FromResult<int>(3)); } public async Task<int> MethodOneAsync() { await Task.Delay(10); return await DoSomethingAsync(() => Task.FromResult<int>(3)); } 

Now I looked at IL generated between two calls, and the following is the most important difference:

The first call with Async and Await inside DoSomethingAsync has the following IL:

 <>c.<MethodOneAsync>b__2_0: IL_0000: newobj UserQuery+<>c+<<MethodOneAsync>b__2_0>d..ctor IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldarg.0 IL_0008: stfld UserQuery+<>c+<<MethodOneAsync>b__2_0>d.<>4__this IL_000D: ldloc.0 IL_000E: call System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.Create IL_0013: stfld UserQuery+<>c+<<MethodOneAsync>b__2_0>d.<>t__builder IL_0018: ldloc.0 IL_0019: ldc.i4.m1 IL_001A: stfld UserQuery+<>c+<<MethodOneAsync>b__2_0>d.<>1__state IL_001F: ldloc.0 IL_0020: ldfld UserQuery+<>c+<<MethodOneAsync>b__2_0>d.<>t__builder IL_0025: stloc.1 IL_0026: ldloca.s 01 IL_0028: ldloca.s 00 IL_002A: call System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.Start<<<MethodOneAsync>b__2_0>d> IL_002F: ldloc.0 IL_0030: ldflda UserQuery+<>c+<<MethodOneAsync>b__2_0>d.<>t__builder IL_0035: call System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.get_Task IL_003A: ret 

The second without Async and Await has the following code:

 <>c.<MethodOneAsync>b__2_0: IL_0000: ldc.i4.3 IL_0001: call System.Threading.Tasks.Task.FromResult<Int32> IL_0006: ret 

In addition, the former has the full state machine code for an additional async await call, which is expected.

Important points:

  • To call the Async method, use async () => await SomeActionAsync() , as this is true. Run Async and work with I / O completion ports.
  • In another case, it calls the Threadpool thread to execute the Async method, which is not suitable for asynchronous execution.

I can insert full IL if necessary to understand the difference, but best of all you evaluate the same in Visual Studio or LinqPad to understand the nuances

-one
source share

There is no functional difference between them. The only difference is that Task from SomeActionAsync is returned directly or expected. Stephen Cleary has a good blog post about this , and he recommends a second approach for this trivial event.

The reason the first approach is available is because you might have a non-trivial lambda expression like this:

 public async Task MethodOneAsync() { return await DoSomethingAsync(async () => { var i = _isItSunday ? 42 : 11; var someResult = await SomeActionAsync(i); return await AnotherActionAsync(someResult*i); }); } 

So the difference is the same as the difference between a method with this signature public async Task<int> MyMethod and this public Task<int> MyMethod

-one
source share

The Async / await construct inserts some infrastructure code, which is only useful if there is some code after waiting. Otherwise, he does practically nothing. Your code is equivalent

 public Task MethodThreeAsync() { return DoSomethingAsync(() => SomeActionAsync()); } 

All three methods: true async.

-2
source share

All Articles