Gör så här: Skriva en enkel Parallel.ForEach-loop
Den här artikeln visar hur du använder en Parallel.ForEach loop för att aktivera dataparallellitet över valfri System.Collections.IEnumerable datakälla eller System.Collections.Generic.IEnumerable<T> datakälla.
Kommentar
Den här dokumentationen använder lambda-uttryck för att definiera ombud i PLINQ. Om du inte är bekant med lambda-uttryck i C# eller Visual Basic kan du läsa Lambda-uttryck i PLINQ och TPL.
Exempel
Det här exemplet visar Parallel.ForEach för processorintensiva åtgärder. När du kör exemplet genererar det slumpmässigt 2 miljoner tal och försöker filtrera efter primtal. Det första fallet itererar över samlingen via en for
loop. Det andra fallet itererar över samlingen via Parallel.ForEach. Den resulterande tiden som varje iteration tar visas när programmet är klart.
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
En Parallel.ForEach loop fungerar som en Parallel.For loop. Loopen partitioner källsamlingen och schemalägger arbetet på flera trådar baserat på systemmiljön. Ju fler processorer i systemet, desto snabbare körs den parallella metoden. För vissa källsamlingar kan en sekventiell loop vara snabbare, beroende på källans storlek och vilken typ av arbete loopen utför. Mer information om prestanda finns i Potentiella fallgropar i data och uppgiftsparallellitet.
Mer information om parallella loopar finns i Så här skriver du en enkel Parallel.For-loop.
Om du vill använda loopen Parallel.ForEach med en icke-generisk samling kan du använda Enumerable.Cast tilläggsmetoden för att konvertera samlingen till en allmän samling, som du ser i följande exempel:
Parallel.ForEach(nonGenericCollection.Cast<object>(),
currentElement =>
{
});
Parallel.ForEach(nonGenericCollection.Cast(Of Object), _
Sub(currentElement)
' ... work with currentElement
End Sub)
Du kan också använda Parallell LINQ (PLINQ) för att parallellisera bearbetningen av IEnumerable<T> datakällor. Med PLINQ kan du använda deklarativ frågesyntax för att uttrycka loopbeteendet. Mer information finns i Parallell LINQ (PLINQ).
Kompilera och köra koden
Du kan kompilera koden som ett konsolprogram för .NET Framework eller som ett konsolprogram för .NET Core.
I Visual Studio finns det Visual Basic- och C#-konsolprogrammallar för Windows Desktop och .NET Core.
Från kommandoraden kan du använda .NET CLI-kommandona (till exempel dotnet new console
eller ) eller dotnet new console -lang vb
skapa filen och använda kommandoradskompilatorn för ett .NET Framework-program.
Om du vill köra ett .NET Core-konsolprogram från kommandoraden använder dotnet run
du från mappen som innehåller ditt program.
Tryck på F5 om du vill köra konsolprogrammet från Visual Studio.