以下Func <task <t>&gt;之间有什么区别?异步委托方法?

时间:2016-10-14 05:19:38

标签: c# async-await

如果我有以下方法:

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?

4 个答案:

答案 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 AwaitDoSomethingAsync的第一次通话跟随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调用的完整状态机代码,这是预期的。

要点:

  1. 对于Async方法,请使用async () => await SomeActionAsync(),因为这是真的异步执行并适用于IO完成端口
  2. 在其他情况下,它为异步方法执行调用Threadpool线程,这对异步执行不利
  3. 如果需要了解差异,我可以粘贴完整的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的方法之间的区别相同