使用和不使用异步声明的lambdas之间是否存在差异

Ant*_*ony 27 c# lambda asynchronous async-await

lambdas () => DoSomethingAsync()async () => await DoSomethingAsync()两者都输入时有区别Func<Task>吗?我们应该选择哪一个?何时?

这是一个简单的控制台应用程序

using System;
using System.Threading.Tasks;

namespace asyncDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var demo = new AsyncDemo();
            var task = demo.RunTheDemo();
            task.Wait();

            Console.ReadLine();
        }
    }

    public class AsyncDemo
    { 
        public async Task Runner(Func<Task> action)
        {
            Console.WriteLine(DateTime.Now.ToLongTimeString() + " Launching the action");
            await action();
        }

        private async Task DoSomethingAsync(string suffix)
        {
            await Task.Delay(2000);
            Console.WriteLine(DateTime.Now.ToLongTimeString() + " Done something, " + suffix);
        }

        public async Task RunTheDemo()
        {
            await Runner(() => DoSomethingAsync("no await"));
            await Runner(async () => await DoSomethingAsync("with await"));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

09:31:08 Launching the action
09:31:10 Done something, no await
09:31:10 Launching the action
09:31:12 Done something, with await
Run Code Online (Sandbox Code Playgroud)

因此RunTheDemo,两个呼叫await Runner(someLambda);似乎都以相同的时序特性做同样的事情 - 两者都有正确的两秒延迟.

这两条线都有效,它们完全相同吗?() => DoSomethingAsync()async () => await DoSomethingAsync()构造之间有什么区别 ?我们应该选择哪一个?何时?

这不是"我应该await在一般情况下使用"这个问题,因为我们正在处理异步代码,并且Func<Task>在消费方法中正确等待lambdas类型.问题涉及如何宣布这些lambdas,以及该声明的影响是什么.

i3a*_*non 20

使用和不使用异步声明的lambdas之间是否存在差异

是的,有区别.一个是异步lambda,另一个是返回任务的lambda.

异步lambda被编译成一个状态机,而另一个则没有,因此异步lambda具有不同的异常语义,因为异常被封装在返回的任务中并且不能同步抛出.

这与常规方法中存在的完全相同.例如,这种异步方法之间:

async Task FooAsync()
{
    await DoSomethingAsync("with await");
}
Run Code Online (Sandbox Code Playgroud)

而这个任务返回方法:

Task FooAsync()
{
    return DoSomethingAsync("no await");
}
Run Code Online (Sandbox Code Playgroud)

查看这些方法可以更清楚地显示差异,但由于lambdas只是语法糖,实际上编译成与这些方法行为相同的方法.

我们应该选择哪一个?何时?

这真的取决于你的口味.使用async关键字会生成一个状态机,其性能不如简单地返回任务.但是,在某些情况下,异常语义可能会令人惊讶.

以此代码为例:

Hamster hamster = null;
Func<Task> asyncAction = () => FooAsync(hamster.Name);

var task = asyncAction();
try
{
    await task;
}
catch
{
    // handle
}
Run Code Online (Sandbox Code Playgroud)

try-catch块会处理NullReferenceException还是不处理?

它不会因为在调用时同步抛出异常 asyncAction.但是,在这种情况下处理异常,因为它在返回的任务中捕获,并在等待该任务时重新抛出.

Func<Task> asyncAction = async () => await FooAsync(hamster.Name);
Run Code Online (Sandbox Code Playgroud)

我个人使用返回任务的lambda作为这一行的表达式lambdas,因为它们通常很简单.但是我的团队在经历了一些非常有害的错误后,总是使用asyncawait关键字.

  • @Luaan [本TryRoslyn示例](http://tryroslyn.azurewebsites.net/#K4Zwlgdg5gBAygTxAFwKYFsDcAoUlaIoYB0AKgBYBOqAhgCb5k0gDWIO2ADsAEYA2YAMYxBfZiBgBhGAG9sMBTG78hMAG4B7MHRgBZABQBKWfMVmAEjXRFKMclZswAvDAjA+fHGbMAxYBEEAHlJmFgA+GGYEAIBBQWQwDQhnSJBo4SNnCJoAdxowZBgfDQ0YtID9e2s0SmIAOStUQy9vBTUaW2RQlKjY+MSIIxaFAF9TBXGlSjB2tBgUGgThENYikrL0/RRp6FdGw0m5VoVqZGBKZLcPYZgxsfllAWFRcRhLatRKeSOzR9Vt/AwBroVCyGBQVDITDzSHQsa3IA==)绝对示出了使用异步-AWAIT时的差: (2认同)
  • 好吧,我认为tl; dr就是: - 它们在大多数方面都是一样的. - 但是异常处理有所不同,如果你需要正确的异常冒泡行为,你宁愿使用带有async ...等待的lambda. - 但是这个版本可能效率较低,因为它会生成一个额外的状态机. (2认同)