簡體   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