跳過到頁腳內容
Iron Academy Logo
C#和.NET中的新特性

Async C#:深入了解使用Tim Corey's Async Zip

Tim Corey
24m 49s

文件壓縮和解壓縮是許多C#開發者的基本任務,不論您是在開發桌面應用程式、網頁伺服器,或僅需對數據進行打包。 多年來,.NET開發者可以同步創建、更新和解壓zip文件,這會阻塞UI執行緒或延遲其他操作。 正如Tim Corey在他的影片".NET 10中的非同步Zip - 一行代碼創建或解壓等更多"中解釋的那樣,.NET 10引入了zip文件的非同步操作,使開發者能編寫高效的代碼,在執行I/O界限的操作時不會凍結主要程式。

C#中的非同步程式設計主要基於Task和Task物件,Task類作為建模正在進行的工作並實現並發執行的核心組件。 Task非同步程式設計(TAP)模型為典型的非同步程式設計提供了一個抽象層,使得處理非同步操作和異常更為容易。 System.Threading.Tasks.Task類及其相關類型用於在C#中實現基於任務的非同步程式設計模型。

在這篇文章中,我們將按照Tim的解釋,展示如何在C#中進行非同步操作來創建、解壓和選擇性地進行zip文件處理,同時使用async/await模式、Task物件和適當的異常處理。

介紹非同步程式設計和.NET 10中的非同步Zip

Tim首先強調了C#多年來創建和解壓zip文件的能力。 然而,隨著.NET 10的推出,開發者現在可以使用非同步代碼來處理這些任務,而不會阻塞調用執行緒。 使用async方法和await關鍵字,操作如創建或解壓大型zip文件可以在背景執行緒上運行,使主要UI執行緒保持回應。

async關鍵字僅是一個修飾符,告訴C#編譯器該方法至少包含一次await關鍵字的使用。 await關鍵字只能在async方法中使用,且其後必須跟隨Task或Task物件。

正如Tim解釋的那樣,這在執行多個任務(如網絡調用、數據庫查询或文件I/O)的應用中尤其重要,因為等待任務的執行可以讓CPU界限的任務繼續執行而不會阻塞調用執行緒。 使用.Result或.Wait()阻塞非同步代碼會將非同步代碼變回同步代碼,可能導致死鎖。

非同步方法返回Task或Task物件,返回任務使得適當的任務組合和並發成為可能。 不含await的方法是同步執行的。 通過從非同步方法返回Task物件,您可以協調多個進行中的操作並更有效地處理異常。

設置專案

Tim首先打開Visual Studio 2026並創建一個名為ZipFilesApp的控制台應用程式。他指出,甚至靜態async Task Main方法也可以用於在程序的入口點執行非同步任務,從而避免阻塞主執行緒。 在適應現有代碼時,您通常可以將同步代碼包裝在Task中或實現非同步版本以在不重寫整個代碼庫的情況下引入非同步行為。

然後他添加了必要的using指令:

using System.IO.Compression;

這樣可以使用如CreateFromDirectoryAsync和ExtractToDirectoryAsync的非同步方法。 在創建調用方法時,重要的是使用一致的命名約定,如對非同步方法追加"Async"後綴,以明確區分它們和同步方法。 不一致的方法命名可能導致對方法是同步還是非同步的誤解。

接下來,Tim準備了三個字串變數來定義zip操作所需的路徑:

  1. 源目錄 – 要壓縮的資料夾。

  2. 目標zip文件 – 用於保存zip文件的完整路徑。

  3. 目標目錄 – 提取的文件將存儲的資料夾。

Tim提到在字串字面的使用@符號以避免轉義反斜杠,這在編寫Windows路徑的同步代碼中是一個常見問題。 在這篇文章中,提供了代碼範例和代碼片段來展示如何在C#中實現非同步程式設計。

使用Async方法創建Zip文件

在3:47,Tim示範了一個使用靜態async Task方法的單行非同步操作:

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

在此,Tim解釋了幾個重要方面:

  • async關鍵字標記此非同步方法,從而啟用async和await。

  • await暫停該非同步方法的執行直到任務完成,允許其他任務或主要UI執行緒繼續運行。 這展示了如何讓await工作並一起暫停執行以確保非同步操作如文件I/O的非阻塞行為。

  • CompressionLevel.SmallestSize確保高效壓縮。

  • includeBaseDirectory參數控制根資料夾是否包含在zip中。

此範例展示了C#中的非同步工作流程如何由async和await關鍵字管理,使代碼更易於維護並簡化異常處理。 await關鍵字只能在async方法內使用,如公用的async Task方法。 當遇到await時,C#編譯器生成一個狀態機來管理執行流,暫停執行,開始非同步任務,並在其完成時恢復。 如果async方法返回一個值,結果可以在如int result的變量中捕獲以供稍後使用。 通過使用async/await,開發者不必手動創建背景執行緒或執行緒池任務,因為編譯器會自動生成所需的狀態機和支持代碼。

非同步提取Zip文件

Tim繼續使用非同步操作提取zip文件,使用另一個async方法:

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

他指出overwriteFiles參數在資料夾已存在的情況下負責異常處理。 默認情況下,如果文件已經存在,提取將失敗,但設置為true會允許非同步操作取代現有文件。

在處理非同步代碼時,健壯的錯誤處理是至關重要的。 Tim強調使用try-catch區塊來處理async方法中的異常是多麼重要。 不適當地處理非同步代碼中的異常可能會導致靜默失敗或意外崩潰,因為非同步任務中拋出的異常可能不會立即顯示。 始終處理異常以確保可靠的應用程式行為。

Tim指出,此async代碼對於多個任務來說是安全的,這意味著它不會阻塞其他同時發生的請求或網絡調用。 使用await關鍵字,調用執行緒在任務結束前會被釋放,創造一個回應的程序流。

此外,當定義async方法時,僅在用於事件處理器(如GUI應用程式中的按鈕點擊)時使用async void。 在這些情況下,方法簽名通常包括(object sender, EventArgs e),其中物件sender標識事件的來源。 在事件處理器之外使用async void可能會導致不可預測的行為和難以診斷的錯誤,因此對於大多數非同步方法應優先使用async Task。

選擇性添加文件到Zip

有時,開發者需要更多的控制而不僅僅是壓縮整個資料夾。 Tim透過創建一個FileStream來展示如何選擇性地進行非同步操作:

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

在非同步添加文件時,每個文件的加入都代表一個特定的任務。 通過同時啟動所有文件添加的任務,然後使用如Task.WhenAll的方法等待其完成來顯著提高效能。 以這種方式管理運行任務,對於高並發場景是必需的,例如當伺服器需要同時處理多個請求時。 這種方法也常用於與遠程伺服器互動,使得應用程式在等待數據時保持回應。

Tim解釋每個細節:

  • FileMode.Create – 確保創建或覆蓋一個新的zip文件。

  • FileAccess.Write – 允許向文件寫入。

  • FileShare.None – 防止在寫入過程中其他任務訪問該文件。

  • useAsync: true – 啟用非同步文件寫入,一個核心的非同步操作。

他指出,在此情境下的非同步程式設計避免了阻塞主執行緒,同時完成任務,這在處理多個任務的Web伺服器上特別有用。 高並發伺服器利用非同步程式設計以高效處理多個同時的客戶端請求,而不會為每個連接生成一個新的執行緒。 然而,順序寫的非同步代碼可能會更難於閱讀和調試,因為async任務可能會不按照順序完成。

創建Zip壓縮檔案並添加文件

接下來,Tim非同步地創建了一個ZipArchive:

using ZipArchive archive = await ZipArchive.CreateAsync(
zipStream, 
ZipArchiveMode.Create, 
leaveOpen: false, 
entryNameEncoding: null
);
  • await表達式確保在任務結束前暫停執行,這使得安全地進行文件添加成為可能。 Async和await簡化了非同步操作的管理,與傳統基於回調的方法相比,代碼更容易閱讀和維護。

  • 重要的是要注意相同的代碼在同步上下文存在的情況下可能表現不同。 例如,在WPF或WinForms應用中,因為同步上下文的存在,await調用後的延續可能運行在主UI執行緒,而在控制台應用中則沒有這樣的上下文。 這可能影響到您的async代碼是如何執行的。

  • 在編寫庫代碼時,考慮在await調用後使用ConfigureAwait(false)來避免不必要的上下文切換並提高性能。

  • 使用Path.GetRelativePath,Tim解釋了如何在zip中保持資料夾結構,這在對多個文件進行非同步工作流程時是必需的:
foreach (string filePath in Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories))
{
string relativePath = Path.GetRelativePath(sourceDirectory, filePath);
await archive.CreateEntryFromFileAsync(filePath, relativePath).ConfigureAwait(false);
}

這展示了实践中的非同步代碼,其中每個文件的添加都是一個非同步調用,await關鍵字確保了正確的順序同時還能執行非阻塞I/O。 非同步代碼允許當前執行緒保持未阻塞,從而提高回應性並允許執行緒在等待I/O操作完成時執行其他工作。

處理文件訪問和異常處理

Tim強調了一個常見的異常處理場景:在原始FileStream任務完成之前嘗試提取zip。 使用文件作用範圍的async void方法或async Task方法而沒有適當的作用範圍using聲明可能會導致:

該過程無法訪問該文件,因為該文件正被另一個進程使用。

解決方案是將非同步操作包裝在一個傳統的作用範圍using區塊中,以在提取之前釋放資源:

await using (FileStream zipStream = new FileStream(...))
{
using ZipArchive archive = await ZipArchive.CreateAsync(zipStream, ...);
// 非同步添加文件
}
// 現在可以安全地提取

這確保了任務物件完成並釋放了同步上下文,避免文件鎖定錯誤。

結論:高效的.NET 10非同步Zip

Tim總結說,.NET 10中的非同步zip操作允許開發者高效地執行非同步操作,避免阻塞主執行緒,並與網絡調用或數據庫查詢等其他任務無縫整合。

使用靜態async Task方法、async修飾符和await表達式,開發者可以:

  • 在不凍結UI執行緒的情況下壓縮或提取zip文件。

  • 在不需要手動執行緒池管理的情況下安全地處理多個執行緒。

  • 在保留資料夾結構的同時進行選擇性壓縮。

  • 確保適當的異常處理和資源清理。

Tim的例子證明在文件操作中使用async/await並不僅僅方便——它是C#中現代非同步程式設計的重要組成部分。

通過遵循他的影片,開發者可以使用async方法、await調用和非同步工作流程來高效地編寫代碼以無縫處理簡單和複雜的文件壓縮任務。

Hero Worlddot related to Async C#:深入了解使用Tim Corey's Async Zip
Hero Affiliate related to Async C#:深入了解使用Tim Corey's Async Zip

通過分享您所愛的東西賺得更多

您是否在為使用.NET、C#、Java、Python或Node.js的開發者創建內容?將您的專業知識轉化為額外收入!

鋼鐵支援團隊

我們每週 5 天,每天 24 小時在線上。
聊天
電子郵件
打電話給我