Nasıl yapılır: Basit bir Parallel.ForEach döngüsü yazma

Bu makalede, herhangi System.Collections.IEnumerable bir Parallel.ForEach veya System.Collections.Generic.IEnumerable<T> veri kaynağı üzerinde veri paralelliğini etkinleştirmek için döngünün nasıl kullanılacağı gösterilmektedir.

Not

Bu belgede PLINQ'ta temsilcileri tanımlamak için lambda ifadeleri kullanılır. C# veya Visual Basic'teki lambda ifadelerini bilmiyorsanız bkz . PLINQ ve TPL'de Lambda ifadeleri.

Örnek

Bu örnekte YOĞUN CPU kullanan işlemler gösterilmektedir Parallel.ForEach . Örneği çalıştırdığınızda rastgele 2 milyon sayı oluşturur ve asal sayıları filtrelemeye çalışır. İlk durum bir for döngü aracılığıyla koleksiyon üzerinde yinelenir. İkinci durum aracılığıyla Parallel.ForEachkoleksiyon üzerinde yinelenir. Uygulama tamamlandığında her yinelemenin aldığı sonuç süresi görüntülenir.

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

Döngü Parallel.ForEach bir Parallel.For döngü gibi çalışır. Döngü, kaynak koleksiyonu bölümler ve sistem ortamına göre birden çok iş parçacığı üzerinde çalışmayı zamanlar. Sistemde ne kadar çok işlemci olursa paralel yöntem o kadar hızlı çalışır. Bazı kaynak koleksiyonlarda, kaynağın boyutuna ve döngünün gerçekleştirdiği çalışma türüne bağlı olarak sıralı döngü daha hızlı olabilir. Performans hakkında daha fazla bilgi için bkz . Verilerde olası tuzaklar ve görev paralelliği.

Paralel döngüler hakkında daha fazla bilgi için bkz . Nasıl yapılır: Basit bir Parallel.For döngüsü yazma.

Döngünün Parallel.ForEach genel olmayan bir koleksiyonla kullanılması için, aşağıdaki örnekte gösterildiği gibi uzantı yöntemini kullanarak Enumerable.Cast koleksiyonu genel bir koleksiyona dönüştürebilirsiniz:

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

Veri kaynaklarının işlenmesini IEnumerable<T> paralelleştirmek için Paralel LINQ (PLINQ) da kullanabilirsiniz. PLINQ, döngü davranışını ifade etmek için bildirim temelli sorgu söz dizimini kullanmanızı sağlar. Daha fazla bilgi için bkz . Parallel LINQ (PLINQ).

Kodu derleme ve çalıştırma

Kodu .NET Framework için bir konsol uygulaması olarak veya .NET Core için bir konsol uygulaması olarak derleyebilirsiniz.

Visual Studio'da, Windows Masaüstü ve .NET Core için Visual Basic ve C# konsol uygulaması şablonları vardır.

Komut satırından .NET CLI komutlarını (örneğin, dotnet new console veya dotnet new console -lang vb) kullanabilir veya dosyayı oluşturup bir .NET Framework uygulaması için komut satırı derleyicisini kullanabilirsiniz.

Komut satırından bir .NET Core konsol uygulaması çalıştırmak için uygulamanızı içeren klasörden kullanın dotnet run .

Konsol uygulamanızı Visual Studio'dan çalıştırmak için F5 tuşuna basın.

Ayrıca bkz.