C# 什么';s以下函数之间的差异<;任务<;T>&燃气轮机;异步委托方法?

C# 什么';s以下函数之间的差异<;任务<;T>&燃气轮机;异步委托方法?,c#,async-await,C#,Async Await,如果我有以下方法: public async Task<T> DoSomethingAsync<T>(Func<Task<T>> action) { // bunch of async code..then "await action()" } 都可以编译,都可以工作,而且没有C#警告 有什么区别(如果有的话)?如果调用者等待,这两个方法是否都将运行真正的异步?async/await构造插入一些基础结构代码,只有在“aw

如果我有以下方法:

public async Task<T> DoSomethingAsync<T>(Func<Task<T>> action)
{
   // bunch of async code..then "await action()"
}
都可以编译,都可以工作,而且没有C#警告


有什么区别(如果有的话)?如果调用者等待,这两个方法是否都将运行真正的异步?

async/await构造插入一些基础结构代码,只有在“await”后面有一些代码时才有用。否则,它基本上什么也不做。您的代码相当于

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

这三种方法都是“真正异步的”。

简短回答

MethodOneAsync()
是真正异步的,应该使用,但是
MethodTwoAsync()
不是真正异步的,因为它调用线程池线程

长答案

为了测试和运行,我将您的代码简化如下:

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

resultTask.Result.Dump();
从Linqpad的方法执行,如下所示:

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));
}
第二个不带
Async和Await
的具有以下代码:

<>c.<MethodOneAsync>b__2_0:
IL_0000:  ldc.i4.3    
IL_0001:  call        System.Threading.Tasks.Task.FromResult<Int32>
IL_0006:  ret      
c.b_uu2_u0:
IL_0000:ldc.i4.3
IL_0001:调用System.Threading.Tasks.Task.FromResult
IL_0006:ret
除此之外,第一个具有额外
async wait
调用的完整状态机代码,这是预期的

要点:

  • 对于异步方法调用,请使用
    Async()=>等待SomeActionAsync()
    ,因为这是真正的异步执行,在IO完成端口上工作
  • 在另一种情况下,它调用
    Threadpool
    线程来执行异步方法,这不利于异步执行

  • 如果需要了解差异,我可以粘贴完整的IL,但最好是您在Visual studio或LinqPad中对其进行评估,以了解细微差别

    两者之间没有功能差异。唯一的区别是直接返回SomeActionAsync的
    任务
    ,或者等待它,并针对这个琐碎的案例推荐了第二种方法

    第一种方法可用的原因是,您可以有一个非平凡的lambda表达式,如下所示:

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

    因此,区别与带有此签名的方法
    公共异步任务MyMethod
    和此方法
    公共任务MyMethod

    之间的区别相同首先,您的示例没有意义,您要么返回了什么,要么没有,返回未返回类型的等待函数的结果将是编译器错误

    public async Task MethodOneAsync()
    {
        return await DoSomethingAsync(() => SomeActionAsync());
    }
    
    其次,这与“真正的”异步无关,因为这将是一个未显示的实现细节

    第三,两个假设例子之间的唯一区别

    await DoSomethingAsync(async () => await SomeActionAsync());
    

    给定
    DoSomethingAsync
    的定义,在第一个示例中,编译器将创建一个额外的
    IAsyncStateMachine
    实现,而不仅仅是转发
    任务。也就是说,更多的编译代码,更多的IL,更多的指令,在这个例子中似乎是多余的

    当省略任务时,有一个关于异常的小警告,但是,因为这只是一个简单的传递,所以没有其他代码会抛出,或者调用额外的状态机或尝试catch和
    任务。不需要FromException


    如果您的签名实际上是一个
    操作
    而不是
    Func
    ,这将在给定异步lambda的情况下创建一个
    async void
    ,那么真正明显的差异就会出现,但是,您的问题并非如此。

    wait
    调用生成的基础结构代码也确保了异常报告中的堆栈帧。如果您依赖于
    Exception.StackTrace
    进行调试,那么您应该使用
    wait
    ,而不仅仅是返回任务。这是错误的,它不仅仅是我的答案中所示的基础结构代码,它是真正的异步和基于线程池的并发之间的区别,这给应用程序及其工作带来了很大的不同,不知道为什么你会被否决。这基本上是正确的,除了一些错误处理问题。@StephenCleary您是否有机会对这个答案发表评论,请?@Pure.Krome,谢谢您的评论,否决票从来都不是问题,但理由很重要,这样我才能学习和纠正。这绝不是异想天开。区别不在于是否涉及线程池线程。唯一的区别是直接返回SomeActionAsync()中的任务,还是等待它。请看斯蒂芬·克利里。对不起,这是错误的。DoSomethingAsync的lambda参数是否异步与阻塞或非阻塞无关。让我们假设DoSomethingAsync和SomeActionAsync是正确实现的异步方法:区别在于lambda是否直接从SomeActionAsync返回任务,或者是否将结果包装在新任务中并返回新任务。生成的IL不同,因为异步lambda创建了一个冗余状态机。
    不同之处在于,在原始示例中,一个实现没有在Func中等待任务,这意味着调用线程永远不会像异步中预期的那样释放,Func只会在任务完成后返回,因此阻止了调用线程。
    这根本不是它的工作方式。任务只是简单地返回。要阻止,您必须执行类似于
    SomeActionAsync().Result
    的操作。是什么阻止了用户将花括号逻辑包装到单独的方法中并使用Async Await,这不是这里提供Async Await的原因,这完全降低了Async Await的值。Async-Await的目的始终是一个非阻塞操作,其中长时间运行的操作可以在后台运行,因为OP已经确认,在功能上两者是相同的,它们确实是相同的,不同之处只会在于长时间运行的方法的性能,这就是为什么Async Await更受欢迎的原因。另外,让我添加基于计算的异步操作
    await DoSomethingAsync(async () => await SomeActionAsync());
    
    await DoSomethingAsync(() =>  SomeActionAsync());