Async C#: Tim Coreyとの非同期Zipへのディープダイブ
ファイルの圧縮と抽出は、多くのC#開発者にとって必須の作業です。デスクトップアプリ、Webサーバーで作業しているか、あるいは単にデータをアーカイブする必要があるかにかかわらず。 長年、.NET開発者は、UIスレッドをブロックしたり、他の操作を遅らせたりすることなく、同期的にzipファイルを作成、更新、抽出することができました。 ティム・コーリーは、".NET 10の非同期Zip – ワンラインで作成または抽出、そしてさらに多く"というビデオで説明していますが、.NET 10では、zipファイルのための非同期操作が導入され、開発者がメインアプリケーションをフリーズさせることなく、I/Oベースの操作を効率よく行うことができるようになりました。
C#の非同期プログラミングは主にTaskとTaskオブジェクトに基づいており、Taskクラスは進行中の作業をモデル化し、同時実行を可能にするためのコアコンポーネントとして機能します。 Task非同期プログラミング(TAP)モデルは、典型的な非同期コーディングに対する抽象化の層を提供し、非同期操作や例外をより簡単に扱えるようにします。 System.Threading.Tasks.Taskクラスおよび関連する型は、C#でタスクベースの非同期プログラミングモデルを実装するために使用されます。
この記事では、ティムの説明に従って、async/awaitパターン、Taskオブジェクト、および適切な例外処理を使用して、C#での非同期操作を実行し、ファイルを作成、抽出、選択的にzipする方法を示します。
.NET 10での非同期プログラミングとAsync Zipの導入
ティムは、C#の長年にわたるzipファイルの作成と抽出の機能を強調します。 しかし、.NET 10では、開発者は呼び出しスレッドをブロックすることなく、非同期コードを使用してこれらのタスクを処理できるようになりました。 asyncメソッドとawaitキーワードを使用することで、大きなzipファイルの作成や抽出などの操作をバックグラウンドスレッドで実行できるため、メインUIスレッドは応答し続けます。
asyncキーワードは、メソッドに少なくとも1つのawaitキーワードが含まれていることをC#コンパイラに伝えるだけのデコレーターです。 awaitキーワードはasyncメソッド内でだけ使用でき、TaskまたはTask
ティムは、これがネットワーク呼び出し、データベースクエリ、ファイルI/Oなどの複数のタスクを実行するアプリケーションにおいて特に重要であり、タスクの待機がCPUバウンドのタスクを続行させる一方で、呼び出しスレッドを拘束しないことを説明します。 .Resultまたは.Wait()で非同期コードをブロックすると、非同期コードが同期コードに戻り、デッドロックが発生する可能性があります。
非同期メソッドはTaskまたはTask
プロジェクトの設定
ティムは、Visual Studio 2026を開いてZipFilesAppというコンソールアプリケーションを作成することから始めます。プログラムのエントリポイントで非同期タスクを実行するために静的async Task Mainメソッドを使用することさえ可能であり、メインスレッドをブロックしません。 既存のコードを適応させる際には、同期コードをTaskでラップしたり、非同期のバリエーションを実装したりすることで、コードベース全体を書き換えなくても非同期の動作を導入することがよくあります。
次に、必須のusingディレクティブを追加します:
using System.IO.Compression;
これにより、CreateFromDirectoryAsyncやExtractToDirectoryAsyncなどの非同期メソッドへのアクセスが可能になります。 呼び出しメソッドを作成する際には、'Async'サフィックスを非同期メソッドに付けるなど、一貫した命名規則を使用し、同期メソッドと非同期メソッドを明確に区別することが重要です。 一貫性のないメソッド命名は、メソッドが同期式なのか非同期式なのかについて混乱を招く可能性があります。
次に、ティムはzip操作に必要なパスを定義するために3つの文字列変数を用意します:
ソースディレクトリ – 圧縮するフォルダ。
デスティネーションzipファイル – zipファイルを保存する完全なパス。
- デスティネーションディレクトリ – 抽出したファイルが保存されるフォルダ。
ティムは、Windowsパスを書き込む際に、同期コード内でバックスラッシュのエスケープを避けるために文字列リテラルで@シンボルを使用することを述べます。 この記事全体を通して、非同期プログラミングをC#で実装する方法を説明するコードサンプルやコードスニペットが提供されています。
非同期メソッドを使用したZipファイルの作成
ティムは3:47で、静的async Taskメソッドを使用したワンライン非同期操作を示します:
await ZipFile.CreateFromDirectoryAsync(
sourceDirectory,
destinationZipFile,
CompressionLevel.SmallestSize,
includeBaseDirectory: false
);
ここで、ティムはいくつかの重要な側面を説明します:
asyncキーワードは、この非同期メソッドをマークし、awaitの使用を可能にします。
awaitは、この非同期メソッドの実行をタスクが終了するまで一時停止させ、他のタスクやメインUIスレッドが実行を続けられるようにします。 これは、非同期操作におけるawaitの作用や一時停止の仕組みを示しており、ファイルI/Oなどの非ブロッキング動作を確保します。
CompressionLevel.SmallestSizeは効率的な圧縮を保証します。
- includeBaseDirectoryパラメータは、zipにルートフォルダを含めるかどうかを制御します。
この例は、C#の非同期ワークフローがasyncとawaitキーワードによってどのように管理されているかを示しており、コードの保守性を高め、例外処理を簡素化します。 awaitキーワードは、公開async Taskメソッドなどのasyncメソッド内でのみ使用できます。 awaitに遭遇すると、C#コンパイラは実行フローを管理するための状態機械を生成し、実行を一時停止し、非同期タスクを開始し、完了すると再開します。 非同期メソッドが値を返す場合、その結果はint resultのように変数にキャプチャされ、後で使用できます。 async/awaitを使用することで、開発者はバックグラウンドスレッドやスレッドプールタスクを手動で作成する必要がありません。コンパイラが必要な状態機械とサポートコードを自動的に生成します。
非同期にAwait Taskを使用してZipファイルを抽出する
ティムは次に、非同期操作を使用してzipファイルを抽出するための他のasyncメソッドを使用します:
await ZipFile.ExtractToDirectoryAsync(
destinationZipFile,
destinationDirectory,
overwriteFiles: false
);
彼は、overwriteFilesパラメータが、フォルダがすでに存在する場合の例外処理を扱うことを指摘します。 デフォルトでは、すでにファイルが存在する場合、抽出は失敗しますが、これをtrueに設定すると非同期操作が既存のファイルを置き換えることができます。
非同期コードで作業する際には、堅牢なエラーハンドリングが重要です。 ティムは、非同期メソッドで例外を処理するためにtry-catchブロックを使用する重要性を強調します。 非同期コードで例外を適切に処理しないと、サイレントな失敗や予期しないクラッシュを引き起こす可能性があります。非同期タスクで発生した例外は、すぐには見えない場合があります。 常に例外を処理して、信頼性のあるアプリケーションの動作を保証してください。
ティムは、この非同期コードが複数のタスクに対して安全であることを指摘し、同時に発生している他のリクエストやネットワーク呼び出しをブロックしません。 awaitキーワードを使用することで、呼び出しスレッドはタスクが終了するまで解放され、応答性のあるプログラムフローが作成されます。
さらに、非同期メソッドを定義する際には、イベントハンドラー、GUIアプリケーション内のボタンクリックなどでのみasync voidを使用することが重要です。 これらの場合、メソッドシグネチャには通常、(object sender, EventArgs e)が含まれており、object senderがイベントのソースを識別します。 イベントハンドラー外でasync voidを使用すると、予測不能な動作や診断が難しいバグにつながる可能性があるため、ほとんどの非同期メソッドではasync Taskを好んで使用してください。
選択的にファイルをZipに追加する
時々、開発者はフォルダ全体を単純に圧縮する以上のコントロールを必要とします。 ティムは、FileStreamを作成して非同期操作を選択的に実行することを示します:
await using FileStream zipStream = new FileStream(
destinationZipFile,
FileMode.Create,
FileAccess.Write,
FileShare.None,
bufferSize: 4096,
useAsync: true
);
ファイルを非同期に追加する際には、各ファイル追加が特定のタスクを表します。 ファイル追加のすべてのタスクを同時に開始し、次にTask.WhenAllなどのメソッドを使用してその完了を待つことで、パフォーマンスを大幅に向上できます。 このように走っているタスクを管理することは、高い同時実行シナリオ、例えばサーバーが同時に複数のリクエストを処理する必要がある場合には不可欠です。 このアプローチは、リモートサーバーとやり取りする際にもよく使用され、アプリケーションがデータを待機中にも応答性を維持します。
ティムはいくつかの詳細を説明します:
FileMode.Create – 新しいzipファイルが作成されるか上書きされることを保証します。
FileAccess.Write – ファイルへの書き込みを許可します。
FileShare.None – 書き込み中に他のタスクがファイルにアクセスするのを防ぎます。
- useAsync: true – 非同期ファイル書き込みを可能にする、コア非同期操作です。
彼は、これにより、タスクが完了する間主スレッドをブロックしない非同期プログラミングがこのコンテキストでどのように回避され、特に多数のタスクを扱うWebサーバーで有用であると述べています。 高コンカレンシサーバーは、各接続のために新しいスレッドを生成する必要がないため、効率的に複数の同時クライアントリクエストを処理するために非同期プログラミングを活用します。 しかし、asyncコードを順番に書くと、読みやすさやデバッグが難しくなり、asyncタスクが順不同に完了することがあります。
Zipアーカイブの作成とファイルの追加
次に、ティムは非同期にZipArchiveを作成します:
using ZipArchive archive = await ZipArchive.CreateAsync(
zipStream,
ZipArchiveMode.Create,
leaveOpen: false,
entryNameEncoding: null
);
await式は、タスクが完了するまで実行を一時停止させ、安全にファイルを追加することを可能にします。 Asyncとawaitは、非同期操作の管理を簡素化し、従来のコールバックベースのアプローチと比較してコードを読みやすく、保守しやすくします。
同じコードが同期コンテキストに応じて異なる動作をする可能性があることを注意してください。 たとえば、WPFやWinFormsアプリケーションでは、同期コンテキストの存在が、awaitコール後の継続をメインUIスレッドで実行させ、一方でコンソールアプリケーションではそんなコンテキストがありません。 これにより、asyncコードの実行方法に影響を与える可能性があります。
ライブラリコードを書くときには、ConfigureAwait(false)をawaitコール後に使用し、不必要なコンテキストスイッチを避け、パフォーマンスを向上させることを考慮してください。
- Path.GetRelativePathを使用して、ディレクトリ構造をzip内に維持する方法をティムが説明しており、これは複数ファイルの非同期ワークフローを実行する際に重要です:
foreach (string filePath in Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories))
{
string relativePath = Path.GetRelativePath(sourceDirectory, filePath);
await archive.CreateEntryFromFileAsync(filePath, relativePath).ConfigureAwait(false);
}
これはプラクティスでのasyncコードを示しており、各ファイル追加が非同期コールであり、awaitキーワードが適切なシーケンシングを保証すると同時に非ブロッキングI/Oを実行します。 非同期コードは、現在のスレッドを非ブロック状態に保つことができ、応答性を向上させ、I/O操作が完了する間に他の作業をスレッドが行うことを可能にします。
ファイルアクセスと例外処理の扱い
ティムは一般的な例外処理シナリオを強調します:元のFileStreamタスクが完了する前にzipを抽出しようとしています。 file-scoped async voidメソッドやasync Taskメソッドを適切なスコープ使用文無しで使用すると、次が引き起こされる可能性があります:
プロセスはファイルにアクセスすることができません。なぜなら、他のプロセスが使用中だからです。解決策は、リソースを解放してから抽出を行うために、伝統的なスコープを持つusingブロックで非同期操作をラップすることです:
await using (FileStream zipStream = new FileStream(...))
{
using ZipArchive archive = await ZipArchive.CreateAsync(zipStream, ...);
// ファイルを非同期で追加
}
// 今は安全に抽出できますこれはタスクオブジェクトが完了し、同期コンテキストを解放することを保証し、ファイルロックエラーを避けます。
結論:.NET 10における効率的な非同期Zip
ティムは、.NET 10の非同期zip操作により、開発者が効率的に非同期操作を実行し、メインスレッドをブロックせず、ネットワーク呼び出しやデータベースクエリのような他のタスクとシームレスに統合することができると結論付けます。
静的async Taskメソッド、asyncモディファイヤ、およびawait式を使用することで、開発者は:
UIスレッドをフリーズさせることなくzipファイルを圧縮または抽出できます。
手動でスレッドプールを管理することなく、複数のスレッドを安全に扱えます。
フォルダー構造を維持しながら選択的に圧縮を行うことができます。
- 適切な例外処理およびリソースクリーンアップを保証します。
ティムの例は、ファイル操作においてasync/awaitが単に便利なだけではなく、現代のC#における非同期プログラミングの不可欠な一部であることを示しています。
彼のビデオに従うことで、開発者は非同期メソッド、await呼び出し、非同期ワークフローを使用して、シンプルおよび複雑なファイル圧縮タスクをシームレスに処理する効率的なコードを書くことができます。

