如果我有以下方法:
public async Task<T> DoSomethingAsync<T>(Func<Task<T>> action)
{
// bunch of async code..then "await action()"
}
以下两种用法有什么区别:
public async Task MethodOneAsync()
{
return await DoSomethingAsync(async () => await SomeActionAsync());
}
public async Task MethodTwoAsync()
{
return await DoSomethingAsync(() => SomeActionAsync());
}
两个编译..都工作..没有R#警告......
有什么区别(如果有的话)?如果调用者等待,两个方法是否都会运行async?
答案 0 :(得分:0)
简答
MethodOneAsync()
是真正的异步,并且应该被使用,但MethodTwoAsync()
不是真正的异步,因为它调用线程池线程
长答案
出于测试和运行的目的,我简化了您的代码,如下所示:
从Linqpad的 Main 方法执行如下:
var resultTask = MethodOneAsync(); // Comment one the methods
resultTask.Result.Dump();
实际代码
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));
}
现在我查看了两个电话之间的IL generated
,以下是最重要的区别:
Async and Await
内DoSomethingAsync
的第一次通话跟随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
没有Async and Await
的第二个代码包含以下代码:
<>c.<MethodOneAsync>b__2_0:
IL_0000: ldc.i4.3
IL_0001: call System.Threading.Tasks.Task.FromResult<Int32>
IL_0006: ret
除此之外,第一个具有额外async await
调用的完整状态机代码,这是预期的。
要点:
async () => await SomeActionAsync()
,因为这是真的异步执行并适用于IO完成端口Threadpool
线程,这对异步执行不利如果需要了解差异,我可以粘贴完整的IL,但最好是在Visual Studio或LinqPad中评估相同的内容
答案 1 :(得分:0)
首先,您的示例没有意义,要么返回某项,要么不返回,返回没有返回类型的等待函数的结果将是编译器错误。
public async Task MethodOneAsync()
{
return await DoSomethingAsync(() => SomeActionAsync());
}
第二,这与“ true”异步无关,因为这将是未显示的实现细节
第三,这两个假设示例之间的唯一区别
await DoSomethingAsync(async () => await SomeActionAsync());
和
await DoSomethingAsync(() => SomeActionAsync());
鉴于DoSomethingAsync
的定义,在第一个示例中,编译器将创建一个额外的IAsyncStateMachine
实现,而不仅仅是转发{ {1}}。即,更多的已编译代码,更多的IL,更多的指令,并且在此示例中看似多余。
在执行清除任务时,会有一个例外的小警告,但是,因为这只是一个简单的传递,所以没有其他代码这样会抛出错误的额外状态机或 try catch ,并且不需要Task
。
如果您的签名实际上是Task.FromException
而不是Action
,那么在给定异步lambda的情况下会创建Func<Task>
的地方,就会出现真正的明显差异。 ,但这不是您的问题。
答案 2 :(得分:-1)
Async / await构造插入一些基础设施代码,只有在&#34; await&#34;之后有一些代码时才有用。否则它基本上什么都不做。您的代码等同于
public Task MethodThreeAsync()
{
return DoSomethingAsync(() => SomeActionAsync());
}
所有这三种方法都是&#34;真正的异步&#34;。
答案 3 :(得分:-1)
两者之间在功能上没有区别。唯一的区别是,是直接返回来自SomeActionAsync的"
还是等待它。 Stephen Cleary has a good blog post about this,并为这种琐碎的情况推荐第二种方法。
第一种方法可用的原因是,您可能会有一个不平凡的lambda表达式,如下所示:
Task
因此,区别与该签名public async Task MethodOneAsync()
{
return await DoSomethingAsync(async () => {
var i = _isItSunday ? 42 : 11;
var someResult = await SomeActionAsync(i);
return await AnotherActionAsync(someResult*i);
});
}
和该签名public async Task<int> MyMethod
的方法之间的区别相同