Poza / dla I/O, .NET udostępnia do — uruchamiania obliczeń na wielu rdzeniach. Kluczowe rozróżnienie: / służy do współbieżności I/O (nieblokujące czekanie), podczas gdy , i PLINQ służą do paralelizacji pracy intensywnie korzystającej z CPU.
Poza / dla I/O, .NET udostępnia do — uruchamiania obliczeń na wielu rdzeniach. Kluczowe rozróżnienie: / służy do współbieżności I/O (nieblokujące czekanie), podczas gdy , i PLINQ służą do paralelizacji pracy intensywnie korzystającej z CPU.
asyncawaitasyncawaitTask.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 planuje pracę na wątku z puli wątków — użyj tego do paralelizacji obliczeń związanych z CPU (NIE dla I/O, gdzie async/await już wystarczy bez dodatkowych wątków).
// 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 i PLINQ (.AsParallel()) automatycznie rozprowadzają pracę na rdzeniach — świetne rozwiązanie dla przetwarzania intensywnie korzystającego z CPU niezależnych elementów. (Warte opłaty tylko dla rzeczywiście powiązanej z CPU, znacznej pracy — są koszty ogólne.)
// 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
Zrozumienie Tasks i równoległości poza async/await to ważna wiedza na poziomie senior do budowania wydajnych aplikacji C#, a kluczowe rozróżnienie między współbieżnością I/O a równoległością CPU to kluczowy wgląd — powszechny punkt zamieszania. async/await obsługuje współbieżność powiązaną z I/O (zwalnianie wątków podczas czekania), ale nie dodaje równoległości CPU, podczas gdy praca związana z CPU (obliczenia, przetwarzanie danych) korzysta z rzeczywistej równoległości na rdzeniach poprzez Task.Run (przesunięcie pracy CPU do wątków puli wątków), Parallel.For/ForEach i PLINQ (.AsParallel()).
Znajomość tego, które narzędzie pasuje — async/await dla I/O, narzędzia równoległości TPL dla pracy związanej z CPU — jest niezbędna do efektywnej optymalizacji wydajności, ponieważ użycie złego (np. oczekiwanie, że async przyspieszy obliczenia, lub tworzenie wątków dla I/O, które async obsługuje wydajnie) jest nieskuteczne.
Również ważne jest zrozumienie bezpieczeństwa wątków: kod równoległy uzyskujący dostęp do stanu udostępnionego wymaga synchronizacji (lock, Interlocked dla operacji atomowych lub kolekcje bezpieczne dla wątków takie jak ConcurrentDictionary) aby uniknąć wyścigów — krytyczne zagadnienie poprawności w kodzie równoległ.
Zrozumienie rozróżnienia I/O vs CPU, narzędzi równoległości (Task.Run, Parallel, PLINQ) i wymagań bezpieczeństwa wątków jest cenne dla pisania poprawnego, wydajnego równoczesngo i równoległ C#.
Ponieważ nowoczesne aplikacje często potrzebują zarówno współbieżności I/O, jak i równoległości CPU, a ponieważ wybór odpowiedniego podejścia i prawidłowe obsługiwanie bezpieczeństwa wątków to to, co sprawia że kod równoległy jest efektywny i poprawny, to jest ważna, często istniejąca wiedza na poziomie senior dla wrażliwego na wydajność rozwoju C#.