I/O のための async/ を超えて、.NET は のための を提供し、計算を複数のコアにわたって実行できます。重要な区別は、/ が I/O の並行性(ノンブロッキングな待機)のためのものであるのに対し、、、PLINQ は CPU 負荷の高い処理を並列化するためのものだという点です。
awaitasyncawaitTask.RunParallelasync/await → I/O-bound concurrency. Frees the thread during waits (DB, network).
Does NOT add CPU parallelism.
Parallelism → CPU-bound work spread across MULTIPLE CORES (computation, processing).
Use Task.Run, Parallel.For/ForEach, PLINQ.
// run a CPU-intensive computation on a thread pool thread (don't block the caller)
int result = await Task.Run(() => ExpensiveComputation());
// run multiple in parallel and combine
var tasks = items.Select(item => Task.Run(() => Process(item)));
var results = await Task.WhenAll(tasks); // wait for all
Task.Run はスレッドプールのスレッドに処理をスケジュールします。CPU バウンドな計算を並列化するために使います(I/O には使いません。I/O では async/await が追加スレッドなしで既に十分です)。
// Parallel.For/ForEach — process a collection across cores
Parallel.ForEach(items, item => Process(item)); // automatically uses multiple cores
Parallel.For(0, 1000, i => Compute(i));
// PLINQ — parallel LINQ
var results = data.AsParallel().Where(x => IsValid(x)).Select(Transform).ToList();
Parallel.For/ForEach と PLINQ(.AsParallel())は処理を自動的にコアへ分散します。独立した要素の CPU 負荷の高い処理に最適です。(オーバーヘッドがあるため、本当に CPU バウンドでそれなりの規模の処理の場合にのみ価値があります。)
// parallel code accessing SHARED state needs synchronization or thread-safe types
lock (_lock) { _counter++; } // lock for mutual exclusion
Interlocked.Increment(ref _counter); // lock-free atomic
var dict = new ConcurrentDictionary<string, int>(); // thread-safe collection
async/await を超えた Task と並列処理を理解することは、高性能な C# アプリケーションを構築するための重要なシニアレベルの知識であり、I/O の並行性と CPU の並列性の決定的な区別が鍵となる洞察です — これはよく混同される点です。async/await は I/O バウンドな並行性(待機中にスレッドを解放する)を扱いますが、CPU の並列性は追加しません。一方、CPU バウンドな処理(計算、データ処理)は、Task.Run(CPU 処理をスレッドプールのスレッドにオフロードする)、Parallel.For/ForEach、PLINQ(.AsParallel())によるコアをまたいだ実際の並列性の恩恵を受けます。
どのツールが適切か(I/O には async/await、CPU バウンドな処理には TPL の並列化ツール)を知ることは、効果的なパフォーマンス最適化に不可欠です。間違ったものを使うと(例えば async が計算を高速化すると期待したり、async が効率よく扱う I/O のためにスレッドを立ち上げたり)効果がないからです。
同様に重要なのがスレッドセーフティの理解です。共有状態にアクセスする並列コードは、競合状態を避けるために同期(lock、アトミックな操作のための Interlocked、または ConcurrentDictionary のようなスレッドセーフなコレクション)を必要とします — これは並列コードにおける重要な正しさの懸念事項です。
I/O か CPU かの区別、並列化ツール(Task.Run、Parallel、PLINQ)、スレッドセーフティの要件を理解することは、正しく高性能な並行・並列 C# を書くうえで価値があります。
現代のアプリケーションは多くの場合 I/O の並行性と CPU の並列性の両方を必要とし、適切なアプローチの選択とスレッドセーフティの正しい処理こそが並列コードを効果的かつ正しくするものであるため、これはパフォーマンスを重視する C# 開発において重要で、頻繁に関係するシニアレベルの知識です。
ジュニアからシニアまで、詳細な回答付きのIT面接質問ライブラリ。
寄付する