UżYWANIE IRON SUITE

Opanowanie Async/Await C# w .NET 10: Niezbędny przewodnik dla skalowalnych aplikacji

Async/Await C#

Współczesne tworzenie oprogramowania wymaga szybkości, responsywności i nieporównywalnej skalowalności. W świecie aplikacji webowych i rozwiązań enterprise, blokowanie wątku UI lub nadmierne korzystanie z zasobów serwera jest po prostu niedopuszczalne. To właśnie tutaj programowanie asynchroniczne, wspierane przez potężne słowa kluczowe async i await w C#, staje się nie tylko funkcją, ale koniecznym fundamentem architektonicznym.

Dla deweloperów korzystających z wysokowydajnych bibliotek, takich jak pakiet Iron Software do generowania PDF, manipulacji obrazem i OCR, zrozumieniuiuiuiuie pisania kodu asynchronicznego jest kluczowe do tworzenia efektywnego kodu, który wykorzystuje pełną moc .NET Task Parallel Library.

Zagłębimy się w mechanizmy async/await C#, badając, jak ta zmiana paradygmatu przekształca wolne programowanie synchroniczne w asynchroniczne operacje o wysokiej przepustowości, i odnosząc te kluczowe pojęcia do sposobu, w jaki Iron Software pomaga przedsiębiorstwom osiągać maksymalną wydajność.

Podstawy Programowania Asynchronicznego

Przed async i await, operacje asynchroniczne były zarządzane za pomocą uciążliwych wywołań zwrotnych i ręcznych operacji klasy Task, prowadzących do skomplikówanego i podatnego na błędy kodu. Programowanie asynchroniczne w C# uprościło to, umożliwiając deweloperom pisanie kodu, który wygląda jak kod synchroniczny, ale zachowuje się asynchronicznie.

Dwa główne komponenty to:

  1. Słowo kluczowe async: Modyfikator async oznacza metodę jako metodę async, która może zawierać wyrażenia await. Co ważne, oznaczenie metody jako async nie uruchamia jej automatycznie w tle. Jedynie umożliwia kompilatorowi generowanie zaawansowanej maszyny stanowej, która zarządza kontynuacją kodu. Metoda async zazwyczaj zwraca obiekt Task (Task lub Task), aby przedstawić bieżące zadanie asynchroniczne.

  2. Słowo kluczowe await: Słowo kluczowe await jest magicznym komponentem. Gdy napotkane zostanie wyrażenie await, metoda sprawdza, czy oczekiwane zadanie zostało zakończone. Jeśli nie, metoda natychmiast wstrzymuje wykonanie i zwraca kontrolę do metody wywołującej (lub wywołującego). To zwalnia bieżący wątek (często główny wątek lub wątek puli wątków) do obsługi innych żądań lub zadań. Gdy zadanie się kończy, reszta metody jest rejestrowana jako kontynuacja i jest zaplanowana do ponownego wykonania.

Oto podstawowy przykład kodu:

public static async Task<string> DownloadDataAsync(string url)
{
    // The async keyword allows us to use await
    using var client = new HttpClient();

    // await task: Control returns to the caller while the HTTP call happens
    string data = await client.GetStringAsync(url); // I/O-bound 

    // The code after the await expression runs once the task finishes
    return $"Data length: {data.Length}";
}

// Modern entry point for console apps
public static async Task Main(string[] args) 
{
    // This is the static async task main entry point
    var result = await DownloadDataAsync("https://api.example.com/data");
    Console.WriteLine(result);
}
public static async Task<string> DownloadDataAsync(string url)
{
    // The async keyword allows us to use await
    using var client = new HttpClient();

    // await task: Control returns to the caller while the HTTP call happens
    string data = await client.GetStringAsync(url); // I/O-bound 

    // The code after the await expression runs once the task finishes
    return $"Data length: {data.Length}";
}

// Modern entry point for console apps
public static async Task Main(string[] args) 
{
    // This is the static async task main entry point
    var result = await DownloadDataAsync("https://api.example.com/data");
    Console.WriteLine(result);
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks

Public Module Program
    Public Async Function DownloadDataAsync(url As String) As Task(Of String)
        ' The async keyword allows us to use await
        Using client As New HttpClient()
            ' await task: Control returns to the caller while the HTTP call happens
            Dim data As String = Await client.GetStringAsync(url) ' I/O-bound 

            ' The code after the await expression runs once the task finishes
            Return $"Data length: {data.Length}"
        End Using
    End Function

    ' Modern entry point for console apps
    Public Async Function Main(args As String()) As Task
        ' This is the static async task main entry point
        Dim result As String = Await DownloadDataAsync("https://api.example.com/data")
        Console.WriteLine(result)
    End Function
End Module
$vbLabelText   $csharpLabel

Użycie static async task main to nowoczesny standard, eliminujący konieczność blokowania głównego wątku przy użyciu starszych metod, takich jak .Wait() lub .Result.

Wydajność i integracja z produktami Iron Software

Podczas gdy Task jest standardowym typem zwracanym dla kodu async, zaawansowane programowanie async w .NET 10 często używa ValueTask dla znacznych zysków wydajnościowych w "gorących ścieżkach", gdzie synchroniczne ukończenie jest prawdopodobne (np. uzyskanie wartośći z pamięci podręcznej). ValueTask unika alokacji pamięci, co czyni go krytycznym dla aplikacji o wysokiej przepustowości.

Stosowanie operacji asynchronicznych w produktach Iron Software

Produkty Iron Software, takie jak IronOCR (Optical Character Recognition) i IronPDF (Generowanie PDF), są doskonałymi kandydatami do wykorzystania wywołań async. Operacje takie jak konwersja dużego dokumentu HTML do PDF lub skanowanie setek stron obrazów w poszukiwaniu tekstu są często zadaniami obciążającymi CPU lub wiążącymi się z I/O systemu plików, czerpiącymi ogromne korzyści z metod asynchronicznych.

Kiedy korzystasz z metod synchronicznych i asynchronicznych dostarczanych przez Iron Software, zapewniasz, że twoja aplikacja pozostaje wysoce responsywna.

Rozważ użycie IronPDF do utworzenia dokumentu z określonego URL:

public static async Task GeneratePdfFromUrlAsync(string url, string outputFileName)
{
    // 1. Initialize the renderer
    var renderer = new IronPdf.ChromePdfRenderer();

    // Optional: Set rendering options if needed (e.g., margins, headers)
    renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;

    // 2. The core asynchronous operation: Fetch and render the URL content
    // This is an I/O-bound task that releases the calling thread.
    var pdf = await renderer.RenderUrlAsPdfAsync(url);

    // 3. Save the PDF file asynchronously
    await Task.Run(() =>
    {
        // This is the synchronous method you confirmed exists
        pdf.SaveAs(outputFileName);
    });
}
public static async Task GeneratePdfFromUrlAsync(string url, string outputFileName)
{
    // 1. Initialize the renderer
    var renderer = new IronPdf.ChromePdfRenderer();

    // Optional: Set rendering options if needed (e.g., margins, headers)
    renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;

    // 2. The core asynchronous operation: Fetch and render the URL content
    // This is an I/O-bound task that releases the calling thread.
    var pdf = await renderer.RenderUrlAsPdfAsync(url);

    // 3. Save the PDF file asynchronously
    await Task.Run(() =>
    {
        // This is the synchronous method you confirmed exists
        pdf.SaveAs(outputFileName);
    });
}
Imports System.Threading.Tasks
Imports IronPdf

Public Module PdfGenerator
    Public Async Function GeneratePdfFromUrlAsync(url As String, outputFileName As String) As Task
        ' 1. Initialize the renderer
        Dim renderer As New ChromePdfRenderer()

        ' Optional: Set rendering options if needed (e.g., margins, headers)
        renderer.RenderingOptions.PaperSize = Rendering.PdfPaperSize.A4

        ' 2. The core asynchronous operation: Fetch and render the URL content
        ' This is an I/O-bound task that releases the calling thread.
        Dim pdf = Await renderer.RenderUrlAsPdfAsync(url)

        ' 3. Save the PDF file asynchronously
        Await Task.Run(Sub()
                           ' This is the synchronous method you confirmed exists
                           pdf.SaveAs(outputFileName)
                       End Sub)
    End Function
End Module
$vbLabelText   $csharpLabel

PDF generowany przy użyciu metod asynchronicznych

PDF renderowany asynchronicznie

Korzystając z metody asynchronicznej RenderHtmlAsPdfAsync(), zapobiegamy zamarzaniu lub blokowaniu aplikacji podczas przetwarzania dużych dokumentów. To pokazuje, jak efektywnie pisać kod asynchroniczny dla złożonego przetwarzania.

Najlepsze praktyki i przypadki brzegowe

1. Obsługa wielu zadań i I/O

Aby zmaksymalizować efektywność, należy inicjować wiele zadań jednocześnie podczas oczekiwania na niezależne prace I/O, takie jak pobieranie danych z zdalnego serwera czy wykonywanie zapytań do bazy danych.

public async Task<string[]> FetchAllDataAsync(string url1, string url2)
{
    // Creating tasks starts the async operation immediately
    Task<string> taskA = DownloadDataAsync(url1); 
    Task<string> taskB = DownloadDataAsync(url2);

    // Wait for all the tasks to complete simultaneously
    string[] results = await Task.WhenAll(taskA, taskB);
    return results;
}
public async Task<string[]> FetchAllDataAsync(string url1, string url2)
{
    // Creating tasks starts the async operation immediately
    Task<string> taskA = DownloadDataAsync(url1); 
    Task<string> taskB = DownloadDataAsync(url2);

    // Wait for all the tasks to complete simultaneously
    string[] results = await Task.WhenAll(taskA, taskB);
    return results;
}
Option Strict On



Public Async Function FetchAllDataAsync(url1 As String, url2 As String) As Task(Of String())
    ' Creating tasks starts the async operation immediately
    Dim taskA As Task(Of String) = DownloadDataAsync(url1)
    Dim taskB As Task(Of String) = DownloadDataAsync(url2)

    ' Wait for all the tasks to complete simultaneously
    Dim results As String() = Await Task.WhenAll(taskA, taskB)
    Return results
End Function
$vbLabelText   $csharpLabel

To standardowy wzorzec tworzenia zadań, które działają jednocześnie, znacznie przyspieszając czas reakcji aplikacji dzięki wykorzystaniu nieblokujących operacji asynchronicznych.

2. Synchronization Context i ConfigureAwait(false)

Gdy oczekiwane zadanie się kończy, domyślne zachowanie to przechwycenie kontekstu synchronizacji i zapewnienie, że kontynuacja działa na tym samym wątku (jak wątek UI). Jest to kluczowe dla aplikacji UI, ale powoduje zbędne nadmiary w kodzie po stronie serwera lub biblioteki.

Użycie ConfigureAwait(false) informuje środowisko uruchomieniowe, że kod po wywołaniu await może być wykonywany na dowolnych dostępnych wątkach z puli wątków. To krytyczna praktyka dla deweloperów bibliotek, zapewniająca maksymalną wydajność operacji asynchronicznych:

// Critical for shared libraries to avoid deadlocks and improve throughput
var data = await GetVarDataFromRemoteServer().ConfigureAwait(false); 
// This code continues on any thread, improving resource usage.
// Critical for shared libraries to avoid deadlocks and improve throughput
var data = await GetVarDataFromRemoteServer().ConfigureAwait(false); 
// This code continues on any thread, improving resource usage.
' Critical for shared libraries to avoid deadlocks and improve throughput
Dim data = Await GetVarDataFromRemoteServer().ConfigureAwait(False)
' This code continues on any thread, improving resource usage.
$vbLabelText   $csharpLabel

3. Niebezpieczeństwo async void

Jedną z najważniejszych zasad w programowaniu async jest nigdy nie używać async void z wyjątkiem asynchronicznych obsługiwaczy zdarzeń. Na przykład, metoda obsługująca kliknięcie przycisku zazwyczaj używa async void:

private async void Button_Click(object sender, EventArgs e) // event handler
{
    // This is one of the few places async void is acceptable
    await GenerateReportAsync(html);
}
private async void Button_Click(object sender, EventArgs e) // event handler
{
    // This is one of the few places async void is acceptable
    await GenerateReportAsync(html);
}
Private Async Sub Button_Click(sender As Object, e As EventArgs) Handles Button.Click ' event handler
    ' This is one of the few places async void is acceptable
    Await GenerateReportAsync(html)
End Sub
$vbLabelText   $csharpLabel

Wszelkie inne użycie metod async void jest stanowczo odradzane. Ponieważ metoda async void nie może być wyczekiwana, wątek wywołujący nie może śledzić jej zakończenia ani wiarygodnie obsługiwać wyjątków, co powoduje problemy z obsługą błędów. Zawsze zwracaj Task lub Task dla wszystkich innych metod async.

4. Obsługa wyjątków

Solidna obsługa wyjątków jest istotna. Kiedy operacja asynchroniczna kończy się niepowodzeniem (np. wywołanie usługi webowej napotyka błąd), wyjątek jest przechowywany w obiekcie zadania. Gdy oczekujesz na zadanie, wyrażenie await ponownie wyrzuca wyjątek na bieżący wątek (ten, który wznawia kontynuację), pozwalając standardowym blokom try...catch działać skutecznie w obsłudze wyjątków.

Wnioski

Wzorzec async i await w C# to cecha zmienności paradygmatu, przenosząca deweloperów z kruchych metod synchronicznych do odpornych, skalowalnych metod asynchronicznych. Poprzez zrozumieniuiuiuiuie wewnętrznej machiny stanowej i przestrzeganie najlepszych praktyk—takich jak priorytetowanie Task ponad async void, używanie ConfigureAwait(false) w bibliotekach i poprawne wdrażanie obsługi wyjątków—deweloperzy mogą tworzyć aplikacje, które radzą sobie z złożonymi zadaniami przetwarzania (takimi jak w Suite Iron Software) z wyjątkową wydajnością.

Iron Software zobowiązuje się do rozwijania produktów mających na celu wysoką wydajność programowania asynchronicznego w ich rdzeniu, zapewniając, że twoje praktyki pisania kodu prowadzą do maksymalnej przepustowości. Odkryj świat Iron Software i zobacz, jak wykorzystanie przetwarzania zadań asynchronicznych może dramatycznie poprawić szybkość i responsywność twojej aplikacji