Przejdź do treści stopki
Iron Academy Logo
Co nowego w C# i .NET

Asynchroniczna kompresja C#: Szczegółowe spojrzenie na asynchroniczną kompresję z Timem Corey

Tim Corey
24m 49s

Kompresja i ekstrakcja plików to niezbędne zadania dla wielu programistów C#, niezależnie od tego, czy pracujesz nad aplikacją desktopową, serwerem internetowym czy po prostu musisz zarchiwizować dane. Od lat deweloperzy .NET mogli tworzyć, aktualizować i rozpakowywać pliki zip synchronicznie, blokując wątek UI lub opóźniając inne operacje. Jak wyjaśnia Tim Corey w swoim wideo "Async Zip in .NET 10 – One Line Create or Extract And More", .NET 10 wprowadza asynchroniczne operacje dla plików zip, umożliwiając programistom pisanie efektywnego kodu wykonującego operacje związane z I/O bez zamrażania głównej aplikacji.

Programowanie asynchroniczne w C# opiera się głównie na obiektach Task i Task, przy czym klasa Task służy jako główny komponent do modelowania trwających prac i umożliwiania równoczesnego wykonania. Model asynchronicznego programowania opartego na zadaniach (TAP) zapewnia warstwę abstrakcji nad typowym asynchronicznym kodowaniem, ułatwiając obsługę operacji asynchronicznych i wyjątków. Klasa System.Threading.Tasks.Task oraz powiązane typy są używane do implementacji modelu asynchronicznego opartego na zadaniach w C#.

W tym artykule podążymy za wyjaśnieniem Tima i pokażemy, jak wykonywać operacje asynchroniczne w C#, aby tworzyć, rozpakowywać i selektywnie kompresować pliki, wszystko przy użyciu wzorca async/await, obiektów Task oraz poprawnej obsługi wyjątków.

Wprowadzenie do programowania asynchronicznego i Async Zip w .NET 10

Tim zaczyna od podkreślenia długotrwałej zdolności C# do tworzenia i rozpakowywania plików zip. Jednak dzięki .NET 10, programiści mogą teraz używać asynchronicznego kodu do obsługi tych zadań bez blokowania wątku wywołującego. Używając metod asynchronicznych oraz słowa kluczowego await, operacje takie jak tworzenie lub rozpakowywanie dużych plików zip mogą być uruchamiane w wątkach w tle, co pozwala głównemu wątkowi interfejsu użytkownika pozostać responsywnym.

Slowo kluczowe async jest tylko dekoratorem, który informuje kompilator C#, że metoda zawiera co najmniej jedno wystąpienie slowa kluczowego await. Slowo kluczowe await moze byc uzywane tylko wewnatrz metody async i musi byc poprzedzone obiektem typu Task lub Task.

Jak wyjaśnia Tim, jest to szczególnie ważne w aplikacjach wykonujących wiele zadań, takich jak wywołania sieciowe, zapytania do bazy danych lub operacje wejścia/wyjścia plików, gdzie oczekiwanie na zadania pozwala kontynuować zadania CPU bez blokowania wątku wywołującego. Blokowanie kodu asynchronicznego za pomocą .Result lub .Wait() zamienia kod asynchroniczny z powrotem na kod synchroniczny, co może prowadzić do blokad wzajemnych.

Metody asynchroniczne zwracają obiekty Task lub Task, a zwracanie zadań umożliwia prawidłową kompozycję zadań i współbieżność. Metody bez await sa wykonywane synchronicznie. Zwracajac obiekty Task z metod asynchronicznych, mozna koordynowac wiele równoczesnych operacji oraz bardziej efektywnie zarzadzac wyjątkami.

Konfiguracja projektu

Tim zaczyna od otwarcia Visual Studio 2026 i utworzenia aplikacji konsolowej o nazwie ZipFilesApp. Zaznacza, że nawet statyczne asynchroniczne metody Task Main mogą być używane do wykonywania zadań asynchronicznych w punkcie wejścia programu, unikając zablokowania głównego wątku. Podczas dostosowywania istniejacego kodu, czesto mozna opakowac kod synchroniczny w Taski lub zaimplementowac asynchroniczne warianty, aby wprowadzic asynchroniczna prace bez przepisania calej bazy kodu.

Nastepnie dodaje niezbedna dyrektywe using:

using System.IO.Compression;

To umożliwia dostęp do asynchronicznych metod takich jak CreateFromDirectoryAsync i ExtractToDirectoryAsync. Podczas tworzenia metod wywoławczych ważne jest stosowanie spójnych konwencji nazewnictwa, takich jak dodawanie przyrostka 'Async' do metod asynchronicznych, aby wyraźnie odróżnić je od metod synchronicznych. Niespójne nazewnictwo metod może prowadzić do zamieszania co do tego, czy metoda jest synchroniczna, czy asynchroniczna.

Następnie Tim przygotowuje trzy zmienne typu string, aby zdefiniować ścieżki potrzebne do operacji zip:

  1. Katalog źródłowy – Folder do kompresji.

  2. Docelowy plik zip – Pelna sciezka zapisu pliku zip.

  3. Katalog docelowy – Folder, w którym będą przechowywane wyodrębnione pliki.

Tim wspomina o używaniu symbolu @ w literałach ciągów, aby uniknąć ucieczki znaków ukośników wstecznych, co jest częstym problemem w kodzie synchronicznym podczas pisania ścieżek Windows. W całym artykule prezentowane są przykłady kodu i fragmenty kodu, aby zilustrować, jak zaimplementować programowanie asynchroniczne w C#.

Tworzenie pliku Zip za pomocą metody asynchronicznej

O 3:47, Tim demonstruje jednolinijkową operację asynchroniczną korzystając z statycznej metody async Task:

await ZipFile.CreateFromDirectoryAsync(
sourceDirectory, 
destinationZipFile, 
CompressionLevel.SmallestSize,
includeBaseDirectory: false
);

Tim wyjaśnia tutaj kilka ważnych aspektów:

Słowo kluczowe async oznacza tę asynchroniczną metodę, umożliwiając użycie await.

  • await wstrzymuje wykonanie tej metody asynchronicznej, dopóki zadanie nie zostanie zakończone, umożliwiając kontynuowanie działania innym zadaniom lub głównemu wątkowi interfejsu użytkownika. To pokazuje, jak await działa i wstrzymuje wykonanie, aby zapewnić nieblokujące działanie w operacjach asynchronicznych, takich jak wejście/wyjście plików.

CompressionLevel.SmallestSize zapewnia efektywna kompresje.

Parametr includeBaseDirectory kontroluje, czy katalog główny jest uwzględniony w pliku ZIP.

Ten przykład pokazuje, jak asynchroniczny przepływ pracy w C# jest zarządzany za pomocą słów kluczowych async i await, co sprawia, że kod jest bardziej łatwy w utrzymaniu i upraszcza obsługę wyjątków. Slowo kluczowe await moze byc uzywane tylko wewnatrz metody asynchronicznej, takiej jak publiczna metoda async Task. Kiedy napotkano await, kompilator C# generuje maszynę stanów do zarządzania przepływem wykonania, wstrzymując wykonanie, uruchamiając zadanie asynchroniczne i wznawiając po jego zakończeniu. Jesli metoda asynchroniczna zwraca wartosc, rezultat mozna zapisac w zmiennej, takiej jak int result, do pozniejszego wykorzystania. Dzięki wykorzystaniu async/await, programiści nie muszą ręcznie tworzyć wątków w tle ani zadań puli wątków, ponieważ kompilator automatycznie generuje potrzebną maszynę stanową i kod wspierający.

Rozpakowywanie pliku Zip z Await Task Asynchronicznie

Tim przechodzi do rozpakowywania pliku zip przy użyciu operacji asynchronicznych, korzystając z innej metody async:

await ZipFile.ExtractToDirectoryAsync(
destinationZipFile, 
    destinationDirectory, 
    overwriteFiles: false
);

Zwraca uwagę, że parametr overwriteFiles obsługuje obsługę wyjątków w przypadku, gdy folder już istnieje. Domyślnie, rozpakowywanie zakończy się niepowodzeniem, jeśli pliki już istnieją, ale ustawienie tego na true pozwala operacji async zastąpić istniejące pliki.

Podczas pracy z kodem asynchronicznym, solidna obsługa błędów jest kluczowa. Tim podkreśla znaczenie używania bloków try-catch do obsługi wyjątków w metodach async. Nieodpowiednia obsługa wyjątków w kodzie asynchronicznym może prowadzić do cichych niepowodzeń lub nieoczekiwanych awarii, ponieważ wyjątki rzucone w zadaniach asynchronicznych mogą nie być natychmiast widoczne. Zawsze obsługuj wyjątki, aby zapewnić niezawodne działanie aplikacji.

Tim zauważa, że ​​ten kod async jest bezpieczny dla wielu zadań, co oznacza, że ​​nie blokuje innych żądań ani wywołań sieciowych wykonywanych jednocześnie. Korzystanie z słowa kluczowego await, wątek wywołujący jest zwalniany, dopóki zadanie się nie zakończy, tworząc responsywny przepływ programu.

Dodatkowo, przy definiowaniu metod async, ważne jest, aby używać async void tylko do obsługi zdarzeń, takich jak kliknięcia przycisku w aplikacjach interfejsu użytkownika. W takich przypadkach sygnatura metody zazwyczaj zawiera (object sender, EventArgs e), gdzie object sender określa źródło zdarzenia. Korzystanie z async void poza obsługą zdarzeń może prowadzić do nieprzewidywalnego zachowania i trudnych do zdiagnozowania błędów, dlatego preferuj async Task dla większości metod asynchronicznych.

Selektownie Dodawanie Plików do Zip

Czasami programiści potrzebują więcej kontroli niż tylko spakowanie całego folderu. Tim demonstruje wykonywanie operacji asynchronicznych selektywnie, tworząc FileStream:

await using FileStream zipStream = new FileStream(
destinationZipFile, 
    FileMode.Create, 
    FileAccess.Write, 
    FileShare.None, 
    bufferSize: 4096, 
    useAsync: true
);

Podczas asynchronicznego dodawania plików, każde dodanie pliku reprezentuje konkretne zadanie. Rozpoczęcie wszystkich zadań dodawania plików jednocześnie, a następnie oczekiwanie na ich zakończenie przy użyciu metod takich jak Task.WhenAll, może znacząco poprawić wydajność. Zarządzanie uruchomionymi zadaniami w ten sposób jest kluczowe dla scenariuszy o wysokiej współbieżności, takich jak wtedy, gdy serwer musi przetworzyć wiele żądań jednocześnie. To podejście jest również powszechnie stosowane przy interakcji z zdalnym serwerem, pozwalając aplikacjom pozostać responsywnymi podczas oczekiwania na dane.

Tim wyjaśnia każdy szczegół:

  • FileMode.Create – Zapewnia utworzenie lub nadpisanie nowego pliku zip.

  • FileAccess.Write – Pozwala na zapis do pliku.

  • FileShare.None – Zapobiega dostępowi innych zadań do pliku podczas jego zapisywania.

  • useAsync: true – Umożliwia asynchroniczny zapis plików, co jest kluczową operacją async.

Zauważa, że programowanie async w tym kontekście unika blokowania głównego wątku, dopóki zadanie się nie zakończy, co jest szczególnie przydatne na serwerach web obsługujących wiele zadań. Serwery o wysokiej współbieżności wykorzystują programowanie asynchroniczne do wydajnego obsługi wielu jednoczesnych żądań klientów, bez tworzenia nowego wątku dla każdej z nich. Jednak pisanie kodu async sekwencyjnie może utrudnić jego czytanie i debugowanie, ponieważ zadania async mogą zakończyć się w nieokreślonej kolejności.

Tworzenie Archiwum Zip i Dodawanie Plików

Następnie Tim tworzy ZipArchive asynchronicznie:

using ZipArchive archive = await ZipArchive.CreateAsync(
    zipStream, 
    ZipArchiveMode.Create, 
    leaveOpen: false, 
    entryNameEncoding: null
);
  • Wyrażenie await zapewnia, że wykonanie zostaje wstrzymane, dopóki zadanie się nie zakończy, co umożliwia bezpieczne dodawanie plików. Async i await upraszczają zarządzanie operacjami asynchronicznymi, co sprawia, że kod jest łatwiejszy do czytania i utrzymania w porównaniu z tradycyjnymi metodami opartymi na callbackach.

  • Ważne jest, aby zauważyć, że ten sam kod może zachowywać się inaczej w zależności od kontekstu synchronizacji. Na przykład w aplikacjach WPF lub WinForms, obecność kontekstu synchronizacji może spowodować, że kontynuacja po wywołaniu await będzie uruchomiona na głównym wątku UI, podczas gdy w aplikacjach konsolowych nie istnieje taki kontekst. To może wpłynąć na to, jak twój kod async będzie wykonywany.

  • Pisząc kod biblioteki, rozważ użycie ConfigureAwait(false) po wywołaniu await, aby uniknąć niepotrzebnej wymiany kontekstu i poprawić wydajność.

  • Używając Path.GetRelativePath, Tim wyjaśnia, jak utrzymać strukturę folderów wewnątrz zip, co jest istotne przy wykonywaniu asynchronicznych przepływów pracy z wieloma plikami:
foreach (string filePath in Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories))
{
    string relativePath = Path.GetRelativePath(sourceDirectory, filePath);
    await archive.CreateEntryFromFileAsync(filePath, relativePath).ConfigureAwait(false);
}

To pokazuje kod async w praktyce, gdzie każde dodanie pliku jest asynchronicznym wywołaniem, a słowo kluczowe await zapewnia odpowiednie sekwencjonowanie, jednocześnie wykonując nieblokujące wejście/wyjście. Asynchroniczny kod pozwala na pozostanie bieżącego wątku niezablokowanym, poprawiając responsywność i pozwalając wątkowi wykonywać inne zadania podczas oczekiwania na zakończenie operacji I/O.

Obsługa Dostępu do Plików i Obsługa Wyjątków

Tim zwraca uwagę na częsty scenariusz obsługi wyjątków: próba wyodrębnienia zip przed zakończeniem oryginalnego zadania FileStream. Korzystanie z metod async void z zakresem pliku lub metod async Task bez odpowiednich instrukcji using scope może spowodować:

Proces nie może uzyskać dostępu do pliku, ponieważ jest on używany przez inny proces.

Rozwiązaniem jest opakowanie asynchronicznej operacji w tradycyjny blok using scope, aby zwolnić zasoby przed wyodrębnieniem:

await using (FileStream zipStream = new FileStream(...))
{
    using ZipArchive archive = await ZipArchive.CreateAsync(zipStream, ...);
    // Dodaj pliki asynchronicznie
}
// Teraz można bezpiecznie dokonywać wyodrębnienia

To zapewnia zakończenie obiektu zadania i zwolnienie kontekstu synchronizacji, unikając błędów blokady plików.

Zakończenie: Wydajne Async Zip w .NET 10

Tim podsumowuje, że asynchroniczne operacje zip w .NET 10 pozwalają programistom wykonywać asynchroniczne operacje efektywnie, unikać blokowania głównego wątku i integrować się płynnie z innymi zadaniami, takimi jak wywołania sieciowe lub zapytania do bazy danych.

Korzystając z metod async static Task, modyfikatorów async i wyrażeń await, programiści mogą:

  • Kompresować lub wyodrębniać pliki zip bez zawieszania wątku UI.

  • Bezpiecznie obsługiwać wiele wątków bez ręcznego zarządzania pulą wątków.

  • Wykonywać selektywne pakowanie, zachowując struktury folderów.

  • Zapewnić odpowiednią obsługę wyjątków i uwalnianie zasobów.

Przykłady Tima pokazują, że async/await w operacjach na plikach nie jest tylko wygodne to kluczowa część nowoczesnego asynchronicznego programowania w C#.

Śledząc jego video, programiści mogą pisać wydajny kod, używając metod async, wywołań await i asynchronicznych przepływów pracy, aby bezproblemowo obsługiwać zarówno proste, jak i złożone zadania kompresji plików.

Hero Worlddot related to Asynchroniczna kompresja C#: Szczegółowe spojrzenie na asynchroniczną kompresję z Timem Corey
Hero Affiliate related to Asynchroniczna kompresja C#: Szczegółowe spojrzenie na asynchroniczną kompresję z Timem Corey

Zarabiaj więcej, dzieląc się tym, co kochasz

Tworzysz treści dla deweloperów pracujących z .NET, C#, Java, Python, czy Node.js? Zamień swoją wiedzę specjalistyczną na dodatkowy dochód!

Zespol wsparcia Iron

Jestesmy online 24 godziny, 5 dni w tygodniu.
Czat
Email
Zadzwon do mnie