Procedure: Een eenvoudige Parallel.ForEach-lus schrijven
In dit artikel wordt beschreven hoe u een Parallel.ForEach lus gebruikt om parallelle uitvoering van gegevens via een System.Collections.IEnumerable of System.Collections.Generic.IEnumerable<T> meer gegevensbronnen mogelijk te maken.
Notitie
In deze documentatie worden lambda-expressies gebruikt om gedelegeerden in PLINQ te definiëren. Als u niet bekend bent met lambda-expressies in C# of Visual Basic, raadpleegt u Lambda-expressies in PLINQ en TPL.
Opmerking
In dit voorbeeld ziet u hoe cpu-intensieve bewerkingen worden uitgevoerd Parallel.ForEach . Wanneer u het voorbeeld uitvoert, worden er willekeurig 2 miljoen getallen gegenereerd en wordt geprobeerd te filteren op priemgetallen. Het eerste geval doorloopt de verzameling via een for
lus. In het tweede geval wordt de verzameling herhaald via Parallel.ForEach. De resulterende tijd die nodig is voor elke iteratie wordt weergegeven wanneer de toepassing is voltooid.
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
Een Parallel.ForEach lus werkt als een Parallel.For lus. De lus partitioneert de bronverzameling en plant het werk op meerdere threads op basis van de systeemomgeving. Hoe meer processors op het systeem, hoe sneller de parallelle methode wordt uitgevoerd. Voor sommige bronverzamelingen kan een sequentiële lus sneller zijn, afhankelijk van de grootte van de bron en het soort werk dat de lus uitvoert. Zie Potentiële valkuilen in gegevens- en taakparallellisme voor meer informatie over prestaties.
Zie How to: Write a simple Parallel.For loop voor meer informatie over parallelle lussen.
Als u de Parallel.ForEach lus met een niet-algemene verzameling wilt gebruiken, kunt u de Enumerable.Cast extensiemethode gebruiken om de verzameling te converteren naar een algemene verzameling, zoals wordt weergegeven in het volgende voorbeeld:
Parallel.ForEach(nonGenericCollection.Cast<object>(),
currentElement =>
{
});
Parallel.ForEach(nonGenericCollection.Cast(Of Object), _
Sub(currentElement)
' ... work with currentElement
End Sub)
U kunt ook Parallel LINQ (PLINQ) gebruiken om de verwerking van IEnumerable<T> gegevensbronnen te parallelliseren. Met PLINQ kunt u de declaratieve querysyntaxis gebruiken om het lusgedrag uit te drukken. Zie Parallel LINQ (PLINQ) voor meer informatie.
De code compileren en uitvoeren
U kunt de code compileren als consoletoepassing voor .NET Framework of als consoletoepassing voor .NET Core.
In Visual Studio zijn er Visual Basic- en C#-consoletoepassingssjablonen voor Windows Desktop en .NET Core.
Vanaf de opdrachtregel kunt u de .NET CLI-opdrachten (bijvoorbeeld dotnet new console
of dotnet new console -lang vb
) gebruiken of het bestand maken en de opdrachtregelcompilator gebruiken voor een .NET Framework-toepassing.
Als u een .NET Core-consoletoepassing wilt uitvoeren vanaf de opdrachtregel, gebruikt dotnet run
u deze vanuit de map die uw toepassing bevat.
Druk op F5 om uw consoletoepassing uit te voeren vanuit Visual Studio.