繁体   English   中英

C# async/await 在无服务器环境中有什么好处?

[英]What are the benefits of C# async/await in a serverless context?

对于简单调用外部服务或写入数据存储的微服务功能,在 C# 中使用async/await有什么意义吗?

我们在 AWS Lambdas 中编写了相当多的此类内容,并且很难确定 async/await 在这种情况下的实际收益是什么,或者它究竟有什么用处。 对于更传统的 IIS web 服务,异步释放了操作系统中的线程并允许服务器为更多请求提供服务。

但是对于 AWS Lambdas,函数每次执行只处理一个请求(限制为 1000 个同时执行)。 因此,如果我们有一个长时间运行的外部进程或具有显着延迟的外部依赖项,则每次 function 执行将被挂起,直到外部进程完成(假设 Lambda 被同步调用)。

这是一个带有三个处理程序的示例 Lambda,我将其中的第三个放入单独的 Lambda 中,称为“DavidSleep”,它仅表示长期运行的外部依赖项。 当我使用前两个处理程序中的任何一个调用另一个名为“DavidTest”的 Lambda 时,我发现异步/等待版本与缺少异步/等待的版本之间没有功能或性能差异。 这两个函数都需要多个并发 Lambda 执行,并且花费相同的时间。

所以异步版本似乎与无异步版本没有区别,但是有区别吗?

public class Test
{
    private IAmazonLambda lambda;

    public Test()
    {
        lambda = new AmazonLambdaClient();
    }

    [LambdaSerializer(typeof(JsonSerializer))]
    public async Task HandleAsync(Request request)
    {
        Console.WriteLine($"Executing for { request.Name }");
        await lambda.InvokeAsync(new InvokeRequest
        {
            FunctionName = "DavidSleep",
            InvocationType = InvocationType.RequestResponse,
            Payload = JsonConvert.SerializeObject(request)
        });
    }

    [LambdaSerializer(typeof(JsonSerializer))]
    public void Handle(Request request)
    {
        Console.WriteLine($"Executing for { request.Name }");
        lambda.InvokeAsync(new InvokeRequest
        {
            FunctionName = "DavidSleep",
            InvocationType = InvocationType.RequestResponse,
            Payload = JsonConvert.SerializeObject(request)
        }).Wait();
    }

    [LambdaSerializer(typeof(JsonSerializer))]
    public void Sleep(Request request)
    {
        Console.WriteLine($"{ request.Name }{ request.RequestId } begin");
        Thread.Sleep(request.WaitInSeconds * 1000);
        Console.WriteLine($"{ request.Name }{ request.RequestId } end");
    }
}

在无服务器上下文中 async/await 的好处仍然是您可以控制调用线程,释放另一个调用者来使用该线程。 我不知道 AWS 是否会从 1000 次呼叫限制中删除await呼叫,但他们可能会这样做。

对于方法中没有其他异步调用的这些单行样式任务调用,您可以简单地返回任务。 无论 AWS Lambda 1000 调用限制如何,将方法标记为async并调用await增加不必要的开销。

示例:

[LambdaSerializer(typeof(JsonSerializer))]
public Task HandleAsync(Request request)
{
    Console.WriteLine($"Executing for { request.Name }");
    return lambda.InvokeAsync(new InvokeRequest
    {
        FunctionName = "DavidSleep",
        InvocationType = InvocationType.RequestResponse,
        Payload = JsonConvert.SerializeObject(request)
    });
}

这里讨论了两种情况:

  • Lambda 函数中使用的 C# 代码中的 Async-await
  • 异步与同步调用长时间运行的 lambda 函数

Lambda 函数中 C# 代码中的异步/等待模式
Lambda 执行框架并不关心代码是如何执行的——它只是调用 .Net 框架来执行代码并等待执行完成。 而在函数内部,则是通常的 async/await 模式的优点。

如果您异步启动长操作,然后使用主线程执行不依赖长操作结果的不同操作,则 C# 代码中的 Async/await 非常有用。 如果函数中只有一个 long 操作,异步和同步执行是类似的。 使用异步而不是同步没有优势(事实上,正如上面的评论所指出的,使用异步机制可能会有轻微的开销)。

异步调用 lambda 函数与同步调用
该问题描述了这种差异的一个示例 - 使用异步方法和同步方法调用长时间运行的 lambda。 由于正当理由,没有观察到任何变化。

同样,只有当主线程可以做一些不同的事情而不依赖于异步方法的结果时,异步调用才有用。 该示例仅显示了对 lambda 的调用。 因此,在异步和同步情况下,主线程必须等待其执行完成。 它没有利用其他执行的等待时间,因此执行所需的总时间没有差异。

异步场景
假设有 2 个 lambda 函数,DavidSleep 和 JohnSleep,它们需要并行调用,彼此独立。 DavidSleep 执行需要 1 秒,JohnSleep 执行需要 2 秒。

如果这两个都在上面的 HandleAsync() 中被调用,则总执行时间将约为 2 秒(加上异步开销的几毫秒)
如果在上面的 Handle() 中调用这些,则总执行时间将约为 3 秒(加上几毫秒)

Async 和 await 很容易使用,但很难理解。 经过 10 多年的 C# 编程,我仍然无法理解所涉及的每个机制。 让我提几件事: Task.Wait, Task.Run, BlockingCollection, lock statement, ConfigureAwait, CancellationToken, TaskCompletionSource etc

因此,除非您只与非常高级的 C# 程序员打交道,并且是其中之一,否则我会尽可能避免 async/await。 有一条规则,您不应过早优化代码。 如果你有一些你需要结果的东西,async/await 无论如何都不会帮助你,因为一旦你请求结果你仍然被阻止。 但是,您可以通过在后台线程中运行 async/await 来推迟获取结果。

我正在同步编写所有代码。 如果我阻止了我的 UI,那么我知道我可以使用 async/await 来避免它,因为 UI 希望我这样做。 但是 UI 在最终处理之前不会向我显示结果。 这就是为什么我说我被屏蔽了。 仅仅因为该行可能在调试器中执行并不意味着结果实际上会在 UI 中。

当我发现一个实际上阻止我进一步处理我的数据的调用时,我希望它发生在一个单独的线程中,那么使用 async/await 当然是有意义的。

如果你有一个服务器,那就是一个查询,然后将结果发送给客户端,就不需要 async/await,因为对于每一个魔法,如果没有时间旅行,服务器将无法将结果发送给客户端在它完成之前。

但是,如果服务器需要并行处理多个查询并返回结果,则在单独的线程中处理每个结果是有意义的。 因此,您希望在处理结果的任何地方都使用 async/await。 否则,您的整个服务器可能会被阻塞,因为它正在处理单个结果,而其他服务器尝试同时到达,但无法处理,因为服务器仍在忙于获取第一个查询的结果。

当您希望并行执行多个 I/O 调用时,Async await 很有用。 比方说 S3-ReadObject 和 DynamoDb-GetItem。 触发两者然后使用 Task.AwaitAll

当您需要执行多个进程并且使这些进程忙碌的事情(例如线程等)时,使用 async/wait 可以工作。 此外,您可以让它等待,按照您想要的顺序执行,并错开多次执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM