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?
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
Threadpoolthread 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
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
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.