🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide

When you start writing C# code that needs to do things at the same time, two methods often come up:

Task.Run
Parallel.ForEachAsync

At first, they both look like “magic ways to run things in parallel.”
But actually, they are built for different jobs…


This content originally appeared on DEV Community and was authored by Spyros Ponaris

When you start writing C# code that needs to do things at the same time, two methods often come up:

  • Task.Run
  • Parallel.ForEachAsync

At first, they both look like “magic ways to run things in parallel.”
But actually, they are built for different jobs. Let’s explore them step by step.

  1. First things first: what do they do?

🟢 Task.Run

Think of Task.Run as saying:

👉 “Run this one heavy piece of work on a background thread so my app doesn’t freeze.”

It’s great for CPU-bound work like:

  • resizing an image
  • encrypting a file

running a long calculation

🟢 Parallel.ForEachAsync

This one is more like:
👉 “I have a list of things to process. Please handle them in parallel, but don’t overload me—keep it under control.”

It’s built for I/O-bound work like:

  • calling multiple APIs
  • downloading files
  • querying a database

It also lets you set a limit (MaxDegreeOfParallelism) so you don’t spam the server or your own machine.
And yes, it supports CancellationToken out of the box.

  1. 📜 A quick history lesson

When Parallel.ForEach was first introduced in .NET 4 (2010), it was designed for CPU-bound, in-memory operations — things like processing arrays, crunching numbers, or running loops over data in memory.

Back then, calling web services inside Parallel.ForEach was discouraged because:

  • Each request would block a thread while waiting.
  • This caused thread-pool starvation and poor scalability.

That’s why the advice was: “Parallel.ForEach is only for CPU-bound in-memory work.”

🔹 Fast forward to .NET 6 (2021): the .NET team introduced Parallel.ForEachAsync.

Why?

To bring the power of parallel loops to async/await workloads.

To make it safe and efficient to run I/O-bound tasks (like HTTP requests, DB queries, or file I/O) in parallel.

To give developers built-in throttling with MaxDegreeOfParallelism, so you don’t need to hand-roll SemaphoreSlim loops.

So if you’ve heard that “Parallel is only for in-memory data” — that’s true for the old synchronous API, but with .NET 6+, Parallel.ForEachAsync is the recommended way to handle async I/O in parallel.

  1. Examples Example A: Calling APIs in parallel (I/O-bound)

Here’s how you fetch multiple web pages at the same time:

var urls = new[]
{
    "https://api.site.com/page/1",
    "https://api.site.com/page/2",
    "https://api.site.com/page/3"
};

await Parallel.ForEachAsync(
    urls,
    new ParallelOptions { MaxDegreeOfParallelism = 3, CancellationToken = cancellationToken },
    async (url, ct) =>
    {
        var response = await httpClient.GetStringAsync(url, ct);
        Console.WriteLine($"{url} => {response.Length} chars");
    });

👉 At most 3 requests run at the same time.
👉 Each call respects cancellation.
👉 No threads are wasted while waiting.

Example B: Heavy image processing (CPU-bound)

Now let’s say you’re processing a huge image. This is CPU work, not I/O.

var result = await Task.Run(
    () => HeavyImageProcessing(inputImage),
    cancellationToken);

Console.WriteLine($"Processed {result.Count} pixels");

👉 Here, Task.Run makes sure the CPU-heavy task doesn’t block your main thread (like an ASP.NET request or UI).

  1. Mistakes to avoid

❌ Wrapping async calls in Task.Run

// Bad: don’t wrap async I/O

await Task.Run(() => httpClient.GetStringAsync(url));

This just wastes a thread.

❌ Using Parallel.ForEachAsync for CPU-heavy loops

// Bad: don’t do CPU work here

await Parallel.ForEachAsync(files, async (file, ct) =>
{
    var data = ProcessFile(file); // CPU-heavy
});

This eats threads without benefit. Use Task.Run or Parallel.For instead.

  1. Rule of Thumb

Here’s the cheat sheet 📝:

  • Use Parallel.ForEachAsync → many async I/O tasks (API calls, DB queries, file downloads).
  • Use Task.Run → single CPU-heavy job (calculations, image processing).
  • Both → accept a CancellationToken, so you can stop them if needed.

Think of it like this:

🔹 Parallel.ForEachAsync = “lots of async things at once”
🔹 Task.Run = “this one heavy thing, off the main thread”
🔹 CancellationToken = “and I can pull the plug anytime”

  1. Takeaway

Don’t mix them up:

  • If you’re waiting on the network, use Parallel.ForEachAsync.
  • If you’re burning the CPU, use Task.Run.

Both make your code faster and more responsive when used in the right place.

👉 Next step: Try rewriting one of your loops. Ask yourself: is this I/O or CPU? That’s how you’ll know which tool to grab.

📚 References


This content originally appeared on DEV Community and was authored by Spyros Ponaris


Print Share Comment Cite Upload Translate Updates
APA

Spyros Ponaris | Sciencx (2025-09-15T09:43:17+00:00) 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide. Retrieved from https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/

MLA
" » 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide." Spyros Ponaris | Sciencx - Monday September 15, 2025, https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/
HARVARD
Spyros Ponaris | Sciencx Monday September 15, 2025 » 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide., viewed ,<https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/>
VANCOUVER
Spyros Ponaris | Sciencx - » 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/
CHICAGO
" » 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide." Spyros Ponaris | Sciencx - Accessed . https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/
IEEE
" » 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide." Spyros Ponaris | Sciencx [Online]. Available: https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/. [Accessed: ]
rf:citation
» 🚀 Parallel.ForEachAsync vs Task.Run in C#: A Beginner’s Guide | Spyros Ponaris | Sciencx | https://www.scien.cx/2025/09/15/%f0%9f%9a%80-parallel-foreachasync-vs-task-run-in-c-a-beginners-guide/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.