Bagikan melalui


Cara: Menulis Pengulangan sederhana Paralel.ForEach

Artikel ini memperlihatkan cara menggunakan perulangan Parallel.ForEach untuk mengaktifkan paralelisme data atas sumber data atau System.Collections.Generic.IEnumerable<T> apa punSystem.Collections.IEnumerable.

Catatan

Dokumentasi ini menggunakan ekspresi lambda untuk menentukan delegasi di PLINQ. Jika Anda tidak terbiasa dengan ekspresi lambda di C# atau Visual Basic, lihat Ekspresi Lambda di PLINQ dan TPL.

Contoh

Contoh ini menunjukkan Parallel.ForEach operasi intensif CPU. Saat Anda menjalankan contoh, contoh tersebut secara acak menghasilkan 2 juta angka dan mencoba memfilter ke angka utama. Kasus pertama mengulangi koleksi melalui perulangan for. Kasus kedua melakukan iterasi atas koleksi melalui Parallel.ForEach. Waktu proses yang dibutuhkan oleh setiap pengulangan ditampilkan ketika aplikasi selesai.

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

Pengulangan Parallel.ForEach bekerja seperti pengulangan Parallel.For. Pengulangan mempartisi kumpulan sumber dan menjadwalkan pekerjaan pada beberapa utas berdasarkan lingkungan sistem. Semakin banyak prosesor pada sistem, semakin cepat metode paralel berjalan. Untuk beberapa koleksi sumber, perulangan berurutan mungkin lebih cepat, tergantung pada ukuran sumber dan jenis pekerjaan yang dilakukan perulangan. Untuk informasi selengkapnya tentang performa, lihat Potensi perangkap dalam paralelisme data dan tugas.

Untuk informasi selengkapnya tentang pengulangan paralel, lihat Cara: Menulis pengulangan sederhana Paralel.For.

Untuk menggunakan perulangan Parallel.ForEach dengan koleksi non-generik, Anda dapat menggunakan Enumerable.Cast metode ekstensi untuk mengonversi koleksi ke koleksi generik, seperti yang ditunjukkan dalam contoh berikut:

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

Anda juga dapat menggunakan Parallel LINQ (PLINQ) untuk menyejajarkan pemrosesan IEnumerable<T> sumber data. PLINQ memungkinkan Anda menggunakan sintaks kueri deklaratif untuk mennyatakan perilaku pengulangan. Untuk informasi selengkapnya, lihat LINQ Paralel (PLINQ).

Kompilasi dan jalankan kode tersebut

Anda dapat mengompilasi kode sebagai aplikasi konsol untuk .NET Framework atau sebagai aplikasi konsol untuk .NET Core.

Di Visual Studio, ada templat aplikasi konsol Visual Basic dan C# untuk Windows Desktop dan .NET Core.

Dari baris perintah, Anda dapat menggunakan perintah .NET CLI (misalnya, dotnet new console atau dotnet new console -lang vb) atau membuat file dan menggunakan compiler baris perintah untuk aplikasi .NET Framework.

Untuk menjalankan aplikasi konsol .NET Core dari baris perintah, gunakan dotnet run dari folder yang berisi aplikasi Anda.

Untuk menjalankan aplikasi konsol dari Visual Studio, tekan F5.

Lihat juga