【发布时间】:2016-03-25 07:44:30
【问题描述】:
为简单起见,让我们假设我们有一个方法应该在执行一些繁重的操作时返回一个对象。有两种实现方式:
public Task<object> Foo()
{
return Task.Run(() =>
{
// some heavy synchronous stuff.
return new object();
}
}
和
public async Task<object> Foo()
{
return await Task.Run(() =>
{
// some heavy stuff
return new object();
}
}
检查生成的 IL 后,生成了两个完全不同的东西:
.method public hidebysig
instance class [mscorlib]System.Threading.Tasks.Task`1<object> Foo () cil managed
{
// Method begins at RVA 0x2050
// Code size 42 (0x2a)
.maxstack 2
.locals init (
[0] class [mscorlib]System.Threading.Tasks.Task`1<object>
)
IL_0000: nop
IL_0001: ldsfld class [mscorlib]System.Func`1<object> AsyncTest.Class1/'<>c'::'<>9__0_0'
IL_0006: dup
IL_0007: brtrue.s IL_0020
IL_0009: pop
IL_000a: ldsfld class AsyncTest.Class1/'<>c' AsyncTest.Class1/'<>c'::'<>9'
IL_000f: ldftn instance object AsyncTest.Class1/'<>c'::'<Foo>b__0_0'()
IL_0015: newobj instance void class [mscorlib]System.Func`1<object>::.ctor(object, native int)
IL_001a: dup
IL_001b: stsfld class [mscorlib]System.Func`1<object> AsyncTest.Class1/'<>c'::'<>9__0_0'
IL_0020: call class [mscorlib]System.Threading.Tasks.Task`1<!!0> [mscorlib]System.Threading.Tasks.Task::Run<object>(class [mscorlib]System.Func`1<!!0>)
IL_0025: stloc.0
IL_0026: br.s IL_0028
IL_0028: ldloc.0
IL_0029: ret
}
和
.method public hidebysig
instance class [mscorlib]System.Threading.Tasks.Task`1<object> Foo () cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = (
01 00 1a 41 73 79 6e 63 54 65 73 74 2e 43 6c 61
73 73 31 2b 3c 42 61 72 3e 64 5f 5f 31 00 00
)
.custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2088
// Code size 59 (0x3b)
.maxstack 2
.locals init (
[0] class AsyncTest.Class1/'<Foo>d__1',
[1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object>
)
IL_0000: newobj instance void AsyncTest.Class1/'<Foo>d__1'::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldarg.0
IL_0008: stfld class AsyncTest.Class1 AsyncTest.Class1/'<Foo>d__1'::'<>4__this'
IL_000d: ldloc.0
IL_000e: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object>::Create()
IL_0013: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object> AsyncTest.Class1/'<Foo>d__1'::'<>t__builder'
IL_0018: ldloc.0
IL_0019: ldc.i4.m1
IL_001a: stfld int32 AsyncTest.Class1/'<Foo>d__1'::'<>1__state'
IL_001f: ldloc.0
IL_0020: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object> AsyncTest.Class1/'<Foo>d__1'::'<>t__builder'
IL_0025: stloc.1
IL_0026: ldloca.s 1
IL_0028: ldloca.s 0
IL_002a: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object>::Start<class AsyncTest.Class1/'<Foo>d__1'>(!!0&)
IL_002f: ldloc.0
IL_0030: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object> AsyncTest.Class1/'<Foo>d__1'::'<>t__builder'
IL_0035: call instance class [mscorlib]System.Threading.Tasks.Task`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<object>::get_Task()
IL_003a: ret
}
正如您在第一种情况下看到的那样,逻辑很简单,创建 lambda 函数,然后生成对 Task.Run 的调用并返回结果。在AsyncTaskMethodBuilder 的第二个示例实例中创建,然后实际构建并返回任务。由于我一直希望 foo 方法在更高级别被称为await Foo(),所以我一直使用第一个示例。但是,我经常看到后者。那么哪种方法是正确的呢?各有什么优缺点?
现实世界的例子
假设我们有 UserStore,它有方法 Task<User> GetUserByNameAsync(string userName),在 web api 控制器中使用,例如:
public async Task<IHttpActionResult> FindUser(string userName)
{
var user = await _userStore.GetUserByNameAsync(userName);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
Task<User> GetUserByNameAsync(string userName) 的哪个实现是正确的?
public Task<User> GetUserByNameAsync(string userName)
{
return _dbContext.Users.FirstOrDefaultAsync(user => user.UserName == userName);
}
或
public async Task<User> GetUserNameAsync(string userName)
{
return await _dbContext.Users.FirstOrDefaultAsync(user => user.UserName == username);
}
【问题讨论】:
-
投反对票?好吧,我至少想听听这个问题有什么问题
标签: c# asynchronous async-await task-parallel-library task