Parallel Foreach Loop in C#
Similar to a regular foreach, which can happen in parallel, is a parallel foreach. It is helpful when we wish to go through a collection iteratively and must put in a fair amount of effort on each item. Given that we have already demonstrated that this kind of work is optimal for the parallel class, it should be evident that the work ahead of us is CPU-bound.
A parallel version of the conventional, sequential Foreach loop is offered by the C# Parallel.foreach. Every iteration of a conventional Foreach loop handles a single item from the collection, processing each item one at a time. On the other hand, the Parallel Foreach approach uses several processors or processor cores to carry out several iterations concurrently.
NOTE: To expedite activities when an expensive, independent CPU-bound operation needs to be done for each sequence input, we must employ parallel loops such as the Parallel.for and Parallel.forEach method.
Parallel Foreach Loop Example in C#
Using an example, let's examine the Parallel Foreach Method. We will first create an example using the conventional sequential Foreach loop to gauge how long it will take to execute. Next, we'll construct an identical example using the Parallel ForEach Loop technique and track how long it takes to run through the example.
In the example below, we build a sequential Foreach Loop, which runs a laborious operation once for each item in the collection. Range method is iterated through in the code below. Calls to the DoSomeIndependentTimeconsumingTask function occur during each iteration.
Example using Standard Foreach Loop in C#:
The following program will be:
Program:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ParallelProgrammingDemo
{
class Program
{
static void Main()
{
Stopwatch stopwatch = new Stopwatch();
Console.WriteLine("Standard Foreach Loop Started");
stopwatch.Start();
List<int> integerList = Enumerable.Range(1, 10).ToList();
foreach (int i in integerList)
{
long total = DoSomeIndependentTimeconsumingTask();
Console.WriteLine("{0} - {1}", i, total);
};
Console.WriteLine("Standard Foreach Loop Ended");
stopwatch.Stop();
Console.WriteLine($"Time Taken by Standard Foreach Loop in Miliseconds {stopwatch.ElapsedMilliseconds}");
Console.ReadLine();
}
static long DoSomeIndependentTimeconsumingTask()
{
//Do Some Time Consuming Task here
long total = 0;
for (int i = 1; i < 100000000; i++)
{
total += i;
}
return total;
}
}
}
OUTPUT:
The Output for the following program will be:
![Parallel Foreach Loop in C#](https://static.tutorialandexample.com/csharp/parallel-foreach-loop-in-csharp1.png)
The output above shows how long it takes to execute the introductory Foreach Loop statement—roughly 2305 milliseconds. This is an example of how to rebuild it using the C# Parallel ForEach function.
Using Degree of Parallelism in C# with Parallel Foreach Loop:
Using C#'s Degree of Parallelism, we may specify the maximum number of threads that can execute the parallel foreach loop. The following is the syntax for using C#'s Degree of Parallelism.
The number of concurrent operations carried out by Parallel method calls provided to this ParallelOptions instance depends on the MaxDegreeOfParallelism attribute. The number of concurrent operations is limited to the specified value by a positive property value.
For and ForEach will use as many threads as the underlying scheduler allows by default; hence, modifying MaxDegreeOfParallelism from the default will restrict the number of concurrent jobs that can be used.
![Parallel Foreach Loop in C#](https://static.tutorialandexample.com/csharp/parallel-foreach-loop-in-csharp2.png)
A Parallel.ForEvery loop functions like a parallel.loop for. Depending on the system environment, the loop divides the source collection and assigns the task to various threads. The parallel technique operates more quickly, the more processors the system has. This tutorial, Potential Problems in Data and task parallelism, has more details concerning performance.
The following example demonstrates how to use the cast extension method to transform the collection into a generic collection:
Parallel.ForEach(nonGenericCollection.Cast<object>(),
currentElement =>
{
});
To parallelize the processing of <IEnumerableT> data sources, you can also use Parallel LINQ (PLINQ). Declarative query syntax can be used with PLINQ to specify loop behavior. Please refer to Parallel LINQ (PLINQ) for further details.
When it comes to iterating through a collection while using many cores, which might improve speed, Parallel.ForEeach is typically preferred over regular foreach. Making sure that what you're trying to perform will truly benefit from parallelization is important because, as was already noted, some tasks lend themselves to parallelization. Later on, we'll discuss some of these factors, but for now, let's look at how Parallel.ForEach differs from foreach.
The "Parallel.ForEach" loop in C# is an effective tool that allows programmers to run parallelized iterations over a collection, improving efficiency for CPU-intensive operations. This loop, a Task Parallel Library (TPL) component made available with.NET Framework 4.0, makes it easier to build concurrent and multi-threaded applications.
Typical "for each" loops process each element in a collection one at a time, sequentially. However, parallel processing can dramatically speed up performance using many CPU cores when working with massive datasets or computationally demanding procedures. The "Parallel.ForEach" loop accomplishes this by automatically dividing the collection into threads and allocating the work among them, enabling concurrent processing.
Here's how the "Parallel.ForEach" loop works:
1. Using System.Threading.Tasks:
Ensure you import the correct namespace before using the "Parallel.ForEach" loop to the correct namespace "System.Threading.Tasks."
2. Parallelizing the loop:
You use "Parallel.ForEach" rather than the standard "foreach" keyword and supply the collection you wish to process as the first argument. An "<ActionT>" delegate, the second argument, designates the action on each element.
3. Thread safety and shared resources:
Thread safety with shared resources: Ensure the proper synchronization techniques (such as locks) are employed when working with shared resources inside the loop to avoid race situations and data corruption. Care must be taken while handling these problems because parallel processing may result in competition for shared resources.
4. Load balancing:
To get the best performance, the "Parallel.ForEach" loop automatically balances the load among threads by attempting to divide the iterations among threads as evenly as possible.
5. Handling exceptions:
Exceptions may occur while the loop is operating. The loop will end immediately by default, and the exception will be propagated if any thread runs into an error. The "AggregateException" class can be used to aggregate all exceptions that happened during concurrent execution.
6. Cancellation support:
The loop additionally accepts an extra parameter called "CancellationToken" that you can use to stop the parallel process if necessary. This is helpful, for example, if the user stops the processing early.
7. Asynchronous processing:
To avoid stopping threads and increase efficiency, it is preferable to utilize asynchronous operations and "Parallel.ForEachAsync" rather than "Parallel.ForEach" when the operation inside the loop is I/O-bound.
8. Suitable scenarios:
The "Parallel.ForEach" loop works best for CPU-bound activities where each element's processing may be done separately from other elements. It is not advised for I/O-bound jobs or situations with significant inter-element interdependence since the overhead may outweigh the advantages of parallelization.
Developers can significantly reduce the execution time of computationally time-consuming activities by using "Parallel.ForEach," which will enhance the responsiveness and general effectiveness of the program. It makes concurrent code easier to implement and makes manually maintaining threads less complicated.
To avoid possible risks, developers need to use attention while utilizing "Parallel.ForEach". To avoid data corruption and race circumstances, thread safety and shared resource management should be carefully considered. Furthermore, "Parallel.ForEach" might not be the ideal option for activities that are I/O-bound or when there are significant inter-element dependencies.
Understanding the level of parallelism, handling exceptions correctly, and taking cancellation support into account as necessary are all crucial for getting the most out of the "Parallel.ForEach" loop. When properly applied, this construct can significantly improve the performance of CPU-bound activities, resulting in a more effective and responsive application.
Conclusion:
In Conclusion, by optimizing parallel processing, the C# "Parallel.ForEach" loop is a useful tool for enhancing the performance of CPU-bound operations. It enables programmers to quickly distribute a collection's processing across several threads in parallel, using multi-core platforms.
Finally, the C# "Parallel.ForEach" loop offers an easy way to carry out parallelized iterations over a collection, utilizing multi-core processing for better performance. When utilized properly, it can significantly speed up CPU-intensive activities. Still, to assure accurate and effective parallel execution, developers need to be mindful of potential problems like thread safety and shared resource management.
Overall, the "Parallel.ForEach" loop is a potent addition to the Task Parallel Library for C#, allowing programmers to fully utilize multi-core platforms and achieve significant performance improvements in the right circumstances.