对于只调用外部服务或写入数据存储的微服务功能,在C#中使用 async / await 是否有意义?
我们正在AWS Lambdas中编写相当数量的这些内容,并且很难确定async / await在此上下文中的实际收益是什么,或者确切地说它有用。对于更传统的IIS Web服务,异步可以释放操作系统中的线程,并允许服务器为更多请求提供服务。
但对于AWS Lambdas,这些函数每次执行只处理一个请求(限制为1000次同时执行)。因此,如果我们有一个长时间运行的外部进程或具有显着延迟的外部依赖,则每个函数执行都将挂起,直到外部进程完成(假设同步调用Lambda)。
这是一个带有三个处理程序的示例Lambda,其中第三个我放入一个名为" DavidSleep"它只代表一个长期运行的外部依赖。当我调用另一个名为" DavidTest"使用前两个处理程序中的任何一个,我发现async / await版本和缺少async / await的版本之间没有功能或性能差异。这两个函数都需要多个并发的Lambda执行,并且需要相同的时间。
所以async版本出现与无异步版本没有区别,但有没有?
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");
}
}
答案 0 :(得分:2)
在无服务器上下文中 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)
});
}
答案 1 :(得分:1)
这里讨论了两种情况:
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 秒(加上几毫秒)
答案 2 :(得分:0)
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。否则,您的整个服务器可能会被阻塞,因为它正在处理单个结果,而其他服务器尝试同时到达,但无法处理,因为服务器仍在忙于获取第一个查询的结果。
答案 3 :(得分:-1)
请参阅:AWS Lambda: async C# handler
并且:https://docs.aws.amazon.com/lambda/latest/dg/csharp-handler.html#csharp-handler-async
看起来这是为了程序员的方便,实际上并没有做任何与Lambda的观点不同的事情(即Lambda调用是同步的,线程只是等待直到指定的时间限制,根据上面的SO链接为15分钟)。 / p>
答案 4 :(得分:-3)
当您需要执行多个进程并且使进程繁忙的事情(例如线程等)时,可以使用 async/wait。此外,您可以让它等待,按照您想要的顺序执行,并错开多次执行。