Events
17 Mar, 21 - 21 Mar, 10
Join the meetup series to build scalable AI solutions based on real-world use cases with fellow developers and experts.
Register nowThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
This article shows how to use a Parallel.ForEach loop to enable data parallelism over any System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> data source.
Note
This documentation uses lambda expressions to define delegates in PLINQ. If you aren't familiar with lambda expressions in C# or Visual Basic, see Lambda expressions in PLINQ and TPL.
This example demonstrates Parallel.ForEach for CPU-intensive operations. When you run the example, it randomly generates 2 million numbers and tries to filter to prime numbers. The first case iterates over the collection via a for
loop. The second case iterates over the collection via Parallel.ForEach. The resulting time taken by each iteration is displayed when the application is finished.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace ParallelExample
{
class Program
{
static void Main()
{
// 2 million
var limit = 2_000_000;
var numbers = Enumerable.Range(0, limit).ToList();
var watch = Stopwatch.StartNew();
var primeNumbersFromForeach = GetPrimeList(numbers);
watch.Stop();
var watchForParallel = Stopwatch.StartNew();
var primeNumbersFromParallelForeach = GetPrimeListWithParallel(numbers);
watchForParallel.Stop();
Console.WriteLine($"Classical foreach loop | Total prime numbers : {primeNumbersFromForeach.Count} | Time Taken : {watch.ElapsedMilliseconds} ms.");
Console.WriteLine($"Parallel.ForEach loop | Total prime numbers : {primeNumbersFromParallelForeach.Count} | Time Taken : {watchForParallel.ElapsedMilliseconds} ms.");
Console.WriteLine("Press 'Enter' to exit.");
Console.ReadLine();
}
/// <summary>
/// GetPrimeList returns Prime numbers by using sequential ForEach
/// </summary>
/// <param name="inputs"></param>
/// <returns></returns>
private static IList<int> GetPrimeList(IList<int> numbers) => numbers.Where(IsPrime).ToList();
/// <summary>
/// GetPrimeListWithParallel returns Prime numbers by using Parallel.ForEach
/// </summary>
/// <param name="numbers"></param>
/// <returns></returns>
private static IList<int> GetPrimeListWithParallel(IList<int> numbers)
{
var primeNumbers = new ConcurrentBag<int>();
Parallel.ForEach(numbers, number =>
{
if (IsPrime(number))
{
primeNumbers.Add(number);
}
});
return primeNumbers.ToList();
}
/// <summary>
/// IsPrime returns true if number is Prime, else false.(https://en.wikipedia.org/wiki/Prime_number)
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
private static bool IsPrime(int number)
{
if (number < 2)
{
return false;
}
for (var divisor = 2; divisor <= Math.Sqrt(number); divisor++)
{
if (number % divisor == 0)
{
return false;
}
}
return true;
}
}
}
Imports System.Collections.Concurrent
Namespace ParallelExample
Class Program
Shared Sub Main()
' 2 million
Dim limit = 2_000_000
Dim numbers = Enumerable.Range(0, limit).ToList()
Dim watch = Stopwatch.StartNew()
Dim primeNumbersFromForeach = GetPrimeList(numbers)
watch.Stop()
Dim watchForParallel = Stopwatch.StartNew()
Dim primeNumbersFromParallelForeach = GetPrimeListWithParallel(numbers)
watchForParallel.Stop()
Console.WriteLine($"Classical foreach loop | Total prime numbers : {primeNumbersFromForeach.Count} | Time Taken : {watch.ElapsedMilliseconds} ms.")
Console.WriteLine($"Parallel.ForEach loop | Total prime numbers : {primeNumbersFromParallelForeach.Count} | Time Taken : {watchForParallel.ElapsedMilliseconds} ms.")
Console.WriteLine("Press 'Enter' to exit.")
Console.ReadLine()
End Sub
' GetPrimeList returns Prime numbers by using sequential ForEach
Private Shared Function GetPrimeList(numbers As IList(Of Integer)) As IList(Of Integer)
Return numbers.Where(AddressOf IsPrime).ToList()
End Function
' GetPrimeListWithParallel returns Prime numbers by using Parallel.ForEach
Private Shared Function GetPrimeListWithParallel(numbers As IList(Of Integer)) As IList(Of Integer)
Dim primeNumbers = New ConcurrentBag(Of Integer)()
Parallel.ForEach(numbers, Sub(number)
If IsPrime(number) Then
primeNumbers.Add(number)
End If
End Sub)
Return primeNumbers.ToList()
End Function
' IsPrime returns true if number is Prime, else false.(https://en.wikipedia.org/wiki/Prime_number)
Private Shared Function IsPrime(number As Integer) As Boolean
If number < 2 Then
Return False
End If
For divisor = 2 To Math.Sqrt(number)
If number Mod divisor = 0 Then
Return False
End If
Next
Return True
End Function
End Class
End Namespace
A Parallel.ForEach loop works like a Parallel.For loop. The loop partitions the source collection and schedules the work on multiple threads based on the system environment. The more processors on the system, the faster the parallel method runs. For some source collections, a sequential loop might be faster, depending on the size of the source and the kind of work the loop performs. For more information about performance, see Potential pitfalls in data and task parallelism.
For more information about parallel loops, see How to: Write a simple Parallel.For loop.
To use the Parallel.ForEach loop with a non-generic collection, you can use the Enumerable.Cast extension method to convert the collection to a generic collection, as shown in the following example:
Parallel.ForEach(nonGenericCollection.Cast<object>(),
currentElement =>
{
});
Parallel.ForEach(nonGenericCollection.Cast(Of Object), _
Sub(currentElement)
' ... work with currentElement
End Sub)
You can also use Parallel LINQ (PLINQ) to parallelize the processing of IEnumerable<T> data sources. PLINQ enables you to use declarative query syntax to express the loop behavior. For more information, see Parallel LINQ (PLINQ).
You can compile the code as a console application for .NET Framework or as a console application for .NET Core.
In Visual Studio, there are Visual Basic and C# console application templates for Windows Desktop and .NET Core.
From the command line, you can use the .NET CLI commands (for example, dotnet new console
or dotnet new console -lang vb
) or create the file and use the command-line compiler for a .NET Framework application.
To run a .NET Core console application from the command line, use dotnet run
from the folder that contains your application.
To run your console application from Visual Studio, press F5.
.NET feedback
.NET is an open source project. Select a link to provide feedback:
Events
17 Mar, 21 - 21 Mar, 10
Join the meetup series to build scalable AI solutions based on real-world use cases with fellow developers and experts.
Register nowTraining
Module
Aprenda a crear variables de matriz e iterar los elementos de la matriz.
Documentation
Procedimiento para escribir un bucle Parallel.For simple - .NET
Aprenda a escribir bucles Parallel.For en .NET en los que no tiene que cancelar el bucle, interrumpir las iteraciones del bucle ni mantener ningún estado local del subproceso.
Procedimiento para cancelar un bucle Parallel.For o ForEach - .NET
Cancele un bucle Parallel.For o Parallel.ForEach en .NET proporcionando un objeto de token de cancelación al método en el parámetro ParallelOptions.
Problemas potenciales en el paralelismo de datos y tareas - .NET
Descubra los posibles problemas en el paralelismo de datos y tareas, ya que el paralelismo agrega complejidad que no se encuentra en el código secuencial.
Procedimiento para controlar excepciones en bucles paralelos - .NET
Aprenda a controlar excepciones en bucles paralelos en .NET. Vea un ejemplo de cómo ajustar todas las excepciones del bucle en System.AggregateException.