C# Use the Task Parallel library, including theParallel.For method, PLINQ, Tasks

This is part of the Microsoft C# Exam 70-483 Free Study Guide. This part of the guide covers the following topics:

More Free Study Guides

If you are looking for a practice quiz, I strongly urge you to take Pluralsight's C# Skill test. If you don't have a subscription you can sign up for a free trial.

Tasks

A C# Task is an asynchronous operation. You can use Task.Run(() => {/* Your Code Here */}) to move code to a Task. This returns a Task that runs on a different thread than the rest of your code. Meanwhile the rest of your code after Task.Run continues to run on its current thread.

You can use the await keyword to get the result, await Task.Run(() => {/* Your Code Here */}) or you can use ContinueWith Task.Run(() => {/* Your Code Here */}).ContinueWith(t => {/* Your Code Here */}). Using ContinueWith is not recommended as shown in this summary of a David Fowler tweet. If you do use ContinueWith you may need to add a .Wait() afterwards to prevent your app from exiting if the ContinueWith has not finished.

You can assign your Task to a variable var longTask = Task.Run() and then use either await variableName or the combination of longTask.ContinueWith() and then eventually longTask.Wait() once you need to confirm the Task is finished before the program moves on or exits.

Task Parallel Library

The goal of the Task Parallel Library is to make it easier to develop parallelism and concurrency in applications.

Parallel code is NOT always faster. Small loops will take longer due to parallel overhead.

A Task represents an asynchronous operation.

Parallel.For method

The Parallel library comes with helpful methods to handle loops and execute multiple statements.

The Parallel.For method works like a for loop but runs the items in parallel using multiple threads to complete the work.

Parallel.For(0, 100, (i) =>
{
  Console.WriteLine($"Item {i}");
  // Writes Item 0 to Item 100 but out of order
});

The Parallel.ForEach method works similar.

public static void Main(string[] args)
{
  var tasks = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  Parallel.ForEach(tasks, (task) => LongRunningTask(task));
}

public static void LongRunningTask(int i)
{
  Thread.Sleep(2000);
  Console.WriteLine($"Task Ran {i}");
}

The above code will finish in less than 20 seconds. If you used a regular foreach loop you would take at least 20 seconds to finish as they would run one at a time. 10 tasks times 2 seconds each.

PLINQ

PLINQ works the same as LINQ but runs tasks in parallel like Parallel.ForEach. You can use AsParallel() in a LINQ statement to run long tasks in parallel. Try running the following with and without AsParallel(). To see differences in performance, adjust Thread.Sleep(1000) or increase the Range(0,20).

public static void Main(string[] args)
{
  var range = Enumerable.Range(0, 20).ToList();
  var evenNumbers = range
    .AsParallel()
    .Where((i) =>
    {
      Thread.Sleep(1000);
      Console.WriteLine(i);
      return i % 2 == 0;
    }).ToList();
}

PLINQ gives you an option to adjust how to handle the chunks with ParallelMergeOptions.

Remember, parallel tasks require a performance startup cost. The longer the task takes, the more value running them in parallel creates. Short tasks may hurt performance.

Other factors that effect performance are:

  • Number of logical CPU cores

  • Number and kind of operations. AsOrdered operator can maintain the sequence but at a cost.

  • Whether the results need to be stored into something like an Array, List, or Enumerable vs running actions.

  • How PLINQ merges results together. Use .WithMergeOptions() after AsParallel() to adjust. NotBuffered returns results sooner but may increase the total time. FullyBuffered delays the first response but may decrease total time. AutoBuffered is somewhere in the middle.

  • Whether the partitions created to split the tasks up are balanced. You can create Custom Partitions to improve this.

One Last Thing...

If you have a question or see a mistake, please comment below.

If you found this post helpful, please share it with others. It's the best thanks I can ask for & it gives me momentum to keep writing!

Matt Ferderer
Software Developer focused on making great user experiences. I enjoy learning, sharing & helping others make amazing things.
Let's Connect