Oltre / per l'I/O, .NET fornisce la per il — eseguire calcoli su più core. La distinzione chiave: / è per la concorrenza I/O (attese non bloccanti), mentre , e PLINQ sono per parallelizzare il lavoro CPU-intensivo.
Oltre / per l'I/O, .NET fornisce la per il — eseguire calcoli su più core. La distinzione chiave: / è per la concorrenza I/O (attese non bloccanti), mentre , e PLINQ sono per parallelizzare il lavoro CPU-intensivo.
asyncawaitasyncawaitTask.RunParallelLa distinzione cruciale: concorrenza vs parallelismo
async/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 pianifica il lavoro su un thread del thread pool — usalo per parallelizzare il calcolo legato alla CPU (NON per l'I/O, dove async/await è già sufficiente senza thread extra).
// 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 e PLINQ (.AsParallel()) distribuiscono automaticamente il lavoro sui core — ottimo per l'elaborazione CPU-intensiva di elementi indipendenti. (Vale la pena solo per il lavoro genuinamente legato alla CPU e di dimensioni significative — c'è overhead.)
// 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
Comprendere i Task e il parallelismo oltre async/await è una conoscenza importante a livello senior per costruire applicazioni C# performanti, e la distinzione cruciale tra la concorrenza I/O e il parallelismo CPU è l'insight chiave — un punto di confusione comune. async/await gestisce la concorrenza I/O-bound (liberando i thread durante le attese) ma non aggiunge parallelismo CPU, mentre il lavoro legato alla CPU (calcoli, elaborazione dati) beneficia dal vero parallelismo tra core tramite Task.Run (offload del lavoro CPU ai thread del pool), Parallel.For/ForEach e PLINQ (.AsParallel()).
Sapere quale strumento è adatto — async/await per l'I/O, gli strumenti di parallelismo TPL per il lavoro legato alla CPU — è essenziale per un'ottimizzazione delle prestazioni efficace, poiché usare quello sbagliato (ad esempio aspettarsi che async velocizzi il calcolo, o avviare thread per l'I/O che async gestisce efficientemente) è inefficace.
Equalmente importante è comprendere la thread safety: il codice parallelo che accede a uno stato condiviso ha bisogno di sincronizzazione (lock, Interlocked per operazioni atomiche, o collezioni thread-safe come ConcurrentDictionary) per evitare race condition — una preoccupazione critica di correttezza nel codice parallelo.
Comprendere la distinzione I/O-vs-CPU, gli strumenti di parallelismo (Task.Run, Parallel, PLINQ) e i requisiti di thread-safety è prezioso per scrivere codice C# concorrente e parallelo corretto e performante.
Poiché le applicazioni moderne spesso necessitano sia di concorrenza I/O che di parallelismo CPU, e poiché scegliere l'approccio giusto e gestire la thread safety correttamente sono ciò che rende il codice parallelo efficace e corretto, questa è una conoscenza importante e frequentemente rilevante a livello senior per lo sviluppo C# sensibile alle prestazioni.