Útmutató: Egyszerű Parallel.ForEach-ciklus írása

Ez a cikk bemutatja, hogyan lehet hurok használatával Parallel.ForEach engedélyezni az adatok párhuzamosságát bármely System.Collections.IEnumerable vagy System.Collections.Generic.IEnumerable<T> adatforráson keresztül.

Feljegyzés

Ez a dokumentáció lambdakifejezésekkel definiálja a meghatalmazottakat a PLINQ-ban. Ha nem ismeri a lambda kifejezéseket a C# vagy a Visual Basic alkalmazásban, tekintse meg a Lambda-kifejezéseket a PLINQ-ban és a TPL-ben.

Példa

Ez a példa a cpu-igényes Parallel.ForEach műveletekre mutat be. A példa futtatásakor véletlenszerűen generál 2 millió számot, és megpróbál prímszámokra szűrni. Az első eset cikluson keresztül viszi át a gyűjteményt for . A második eset a gyűjteményen keresztül Parallel.ForEachiterál. Az alkalmazás befejezésekor az egyes iterációk által kapott idő jelenik meg.

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 any key 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 any key 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 hurok úgy működik, mint egy Parallel.For hurok. A hurok particionálja a forrásgyűjteményt, és a rendszerkörnyezet alapján több szálon ütemezi a munkát. Minél több processzor van a rendszeren, annál gyorsabban fut a párhuzamos metódus. Egyes forrásgyűjtemények esetében a szekvenciális hurok gyorsabb lehet a forrás méretétől és a ciklus által végzett munka típusától függően. A teljesítménnyel kapcsolatos további információkért lásd az adatok és a tevékenységek párhuzamosságának lehetséges buktatóit.

A párhuzamos hurkokkal kapcsolatos további információkért lásd : How to: Write a simple Parallel.For loop.

Ha a Parallel.ForEach hurkot nem általános gyűjteményrel szeretné használni, a Enumerable.Cast bővítménymetódussal átalakíthatja a gyűjteményt általános gyűjteménysé, ahogyan az alábbi példában látható:

Parallel.ForEach(nonGenericCollection.Cast<object>(),
    currentElement =>
    {
    });
Parallel.ForEach(nonGenericCollection.Cast(Of Object), _
                 Sub(currentElement)
                     ' ... work with currentElement
                 End Sub)

A Párhuzamos LINQ (PLINQ) használatával párhuzamossá teheti az adatforrások feldolgozását IEnumerable<T> . A PLINQ lehetővé teszi deklaratív lekérdezési szintaxis használatát a ciklus viselkedésének kifejezéséhez. További információ: Parallel LINQ (PLINQ).

A kód fordítása és futtatása

A kódot .NET-keretrendszer konzolalkalmazásként vagy .NET Core-konzolalkalmazásként fordíthatja le.

A Visual Studióban Visual Basic és C# konzolalkalmazássablonok találhatók a Windows Desktophoz és a .NET Core-hoz.

A parancssorból használhatja a .NET CLI-parancsokat (például) vagy dotnet new console -lang vblétrehozhatja a fájlt, dotnet new console és használhatja a parancssori fordítót egy .NET-keretrendszer-alkalmazáshoz.

Ha .NET Core-konzolalkalmazást szeretne futtatni a parancssorból, használja dotnet run az alkalmazást tartalmazó mappából.

A konzolalkalmazás Visual Studióból való futtatásához nyomja le az F5 billentyűt.

Lásd még