PLINQ'e Giriş

Paralel LINQ (PLINQ), DilLe Tümleşik Sorgu (LINQ) deseninin paralel bir uygulamasıdır. PLINQ, ad alanı için System.Linq uzantı yöntemleri olarak LINQ standart sorgu işleçlerinin tam kümesini uygular ve paralel işlemler için ek işleçlere sahiptir. PLINQ, LINQ söz diziminin basitliğini ve okunabilirliğini paralel programlamanın gücüyle birleştirir.

İpucu

LINQ hakkında bilginiz yoksa, herhangi bir numaralandırılabilir veri kaynağını tür açısından güvenli bir şekilde sorgulamak için birleştirilmiş bir model içerir. LINQ to Objects, ve dizileri gibi List<T> bellek içi koleksiyonlarda çalıştırılacak LINQ sorgularının adıdır. Bu makalede LINQ hakkında temel bilgilere sahip olduğunuz varsayılır. Daha fazla bilgi için bkz . DilLe Tümleşik Sorgu (LINQ).

Paralel sorgu nedir?

PlINQ sorgusu birçok yönden paralel olmayan linq to Objects sorgusuna benzer. AYNı sıralı LINQ sorguları gibi PLINQ sorguları da herhangi bir bellek IEnumerable içi veya IEnumerable<T> veri kaynağı üzerinde çalışır ve yürütmeyi erteler, yani sorgu numaralandırılana kadar yürütülmeye başlamaz. Birincil fark, PLINQ'un sistemdeki tüm işlemcileri tam olarak kullanmayı denemesidir. Bunu yapmak için veri kaynağını bölümlere ayırır ve ardından sorguyu ayrı çalışan iş parçacıklarındaki her kesimde birden çok işlemci üzerinde paralel olarak yürütür. Çoğu durumda paralel yürütme, sorgunun önemli ölçüde daha hızlı çalıştığı anlamına gelir.

Paralel yürütme sayesinde PLINQ, genellikle yalnızca veri kaynağına sorgu işlemini ekleyerek AsParallel belirli sorgu türleri için eski kod üzerinde önemli performans geliştirmeleri elde edebilir. Ancak paralellik kendi karmaşıklıklarına neden olabilir ve tüm sorgu işlemleri PLINQ'ta daha hızlı çalışmaz. Aslında paralelleştirme aslında bazı sorguları yavaşlatır. Bu nedenle, sıralama gibi sorunların paralel sorguları nasıl etkilediğini anlamanız gerekir. Daha fazla bilgi için bkz . PLINQ'te Hızlandırmayı Anlama.

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 İfadeleri.

Bu makalenin geri kalanında ana PLINQ sınıflarına genel bir bakış sunulur ve PLINQ sorgularının nasıl oluşturulacağı açıklanır. Her bölüm, daha ayrıntılı bilgilerin ve kod örneklerinin bağlantılarını içerir.

ParallelEnumerable Sınıfı

sınıfı, System.Linq.ParallelEnumerable PLINQ'un neredeyse tüm işlevlerini kullanıma sunar. Bu ve ad alanı türlerinin System.Linq geri kalanı System.Core.dll derlemesinde derlenir. Visual Studio'daki varsayılan C# ve Visual Basic projeleri hem derlemeye başvurur hem de ad alanını içeri aktarır.

ParallelEnumerable , LINQ to Objects tarafından desteklenen tüm standart sorgu işleçlerinin uygulamalarını içerir, ancak her birini paralelleştirmeye çalışmaz. LINQ hakkında bilginiz yoksa bkz . LINQ'e Giriş (C#) ve LINQ'e Giriş (Visual Basic).

Standart sorgu işleçlerine ek olarak, ParallelEnumerable sınıfı paralel yürütmeye özgü davranışları etkinleştiren bir dizi yöntem içerir. PLINQ'a özgü bu yöntemler aşağıdaki tabloda listelenmiştir.

ParallelEnumerable İşleci Açıklama
AsParallel PLINQ için giriş noktası. Mümkünse sorgunun geri kalanının paralelleştirilmesi gerektiğini belirtir.
AsSequential Sorgunun geri kalanının paralel olmayan bir LINQ sorgusu olarak sıralı olarak çalıştırılması gerektiğini belirtir.
AsOrdered PLINQ'un sorgunun geri kalanı için veya sıralama değiştirilene kadar (örneğin, bir orderby (Visual Basic'te Order By) yan tümcesi kullanarak kaynak dizisinin sıralamasını koruması gerektiğini belirtir.
AsUnordered Kaynak dizinin sırasını korumak için sorgunun geri kalanı için PLINQ'un gerekli olmadığını belirtir.
WithCancellation PLINQ'un sağlanan iptal belirtecinin durumunu düzenli aralıklarla izlemesi ve istenirse yürütmeyi iptal etmesi gerektiğini belirtir.
WithDegreeOfParallelism PLINQ'nin sorguyu paralelleştirmek için kullanması gereken işlemci sayısı üst sınırını belirtir.
WithMergeOptions PLINQ'un mümkünse paralel sonuçları tüketen iş parçacığında tek bir dizide birleştirmesi gerektiği hakkında bir ipucu sağlar.
WithExecutionMode PlINQ'un, varsayılan davranış sorguyu sırayla çalıştırmak olsa bile sorguyu paralelleştirip paralelleştirmeyeceğini belirtir.
ForAll Sorgunun sonuçları üzerinde yinelemeden farklı olarak, sonuçların ilk olarak tüketici iş parçacığıyla birleştirilmeden paralel olarak işlenmesini sağlayan çok iş parçacıklı bir numaralandırma yöntemi.
Aggregate Aşırı PLINQ için benzersiz olan ve iş parçacığı yerel bölümleri üzerinde ara toplamaya olanak tanıyan bir aşırı yükleme ve tüm bölümlerin sonuçlarını birleştirmek için son bir toplama işlevi.

Abone Olma Modeli

Bir sorgu yazarken, aşağıdaki örnekte gösterildiği gibi veri kaynağında uzantı yöntemini çağırarak ParallelEnumerable.AsParallel PLINQ'u kabul edin.

var source = Enumerable.Range(1, 10000);

// Opt in to PLINQ with AsParallel.
var evenNums = from num in source.AsParallel()
               where num % 2 == 0
               select num;
Console.WriteLine("{0} even numbers out of {1} total",
                  evenNums.Count(), source.Count());
// The example displays the following output:
//       5000 even numbers out of 10000 total
Dim source = Enumerable.Range(1, 10000)

' Opt in to PLINQ with AsParallel
Dim evenNums = From num In source.AsParallel()
               Where num Mod 2 = 0
               Select num
Console.WriteLine("{0} even numbers out of {1} total",
                  evenNums.Count(), source.Count())
' The example displays the following output:
'       5000 even numbers out of 10000 total

AsParallel uzantı yöntemi, sonraki sorgu işleçlerini (bu örnekte where ve select) System.Linq.ParallelEnumerable uygulamalara bağlar.

Yürütme Modları

PLINQ varsayılan olarak muhafazakardır. Çalışma zamanında PLINQ altyapısı sorgunun genel yapısını analiz eder. Sorgu paralelleştirmeyle hız kazanma olasılığı yüksekse, PLINQ kaynak dizisini eşzamanlı olarak çalıştırılacak görevlere böler. Bir sorguyu paralel hale getirmek güvenli değilse PLINQ yalnızca sorguyu sıralı olarak çalıştırır. PLINQ'un pahalı olabilecek paralel algoritma ile ucuz bir sıralı algoritma arasında bir seçeneği varsa, varsayılan olarak sıralı algoritmayı seçer. PLINQ'a paralel algoritmayı System.Linq.ParallelExecutionMode seçmesini bildirmek için yöntemini ve numaralandırmasını kullanabilirsinizWithExecutionMode. Bu, belirli bir sorguyu paralel olarak daha hızlı yürütürken test ve ölçüm yaparak bilgi edindiğinizde kullanışlıdır. Daha fazla bilgi için bkz . Nasıl yapılır: PLINQ'ta Yürütme Modunu Belirtme.

Paralellik Derecesi

PLINQ varsayılan olarak konak bilgisayardaki tüm işlemcileri kullanır. yöntemini kullanarak PLINQ'a belirtilen sayıda işlemciden fazlasını kullanmamasını WithDegreeOfParallelism belirtebilirsiniz. Bu, bilgisayarda çalışan diğer işlemlerin belirli bir CPU süresi aldığından emin olmak istediğinizde kullanışlıdır. Aşağıdaki kod parçacığı sorguyu en fazla iki işlemci kullanarak sınırlar.

var query = from item in source.AsParallel().WithDegreeOfParallelism(2)
            where Compute(item) > 42
            select item;
Dim query = From item In source.AsParallel().WithDegreeOfParallelism(2)
            Where Compute(item) > 42
            Select item

Bir sorgunun Dosya G/Ç gibi işlemle ilişkili olmayan önemli miktarda çalışma yaptığı durumlarda, makinedeki çekirdek sayısından daha fazla paralellik derecesi belirtmek yararlı olabilir.

Sıralı ve Sırasız Paralel Sorgular

Bazı sorgularda, sorgu işlecinin kaynak dizinin sırasını koruyan sonuçlar üretmesi gerekir. PLINQ bu amaçla işleci sağlar AsOrdered . AsOrdered ' den AsSequentialfarklıdır. Bir AsOrdered dizi hala paralel olarak işlenir, ancak sonuçları arabelleğe alınıp sıralanır. Sipariş koruması genellikle fazladan çalışma gerektirdiğinden, bir AsOrdered dizi varsayılan AsUnordered diziden daha yavaş işlenebilir. Belirli bir sıralı paralel işlemin, işlemin sıralı sürümünden daha hızlı olup olmadığı birçok faktöre bağlıdır.

Aşağıdaki kod örneği, sipariş korumayı kabul etme işlemini gösterir.

var evenNums =
    from num in numbers.AsParallel().AsOrdered()
    where num % 2 == 0
    select num;
Dim evenNums = From num In numbers.AsParallel().AsOrdered()
               Where num Mod 2 = 0
               Select num


Daha fazla bilgi için bkz . PLINQ'ta Sipariş Koruması.

Paralel ve Sıralı Sorgular karşılaştırması

Bazı işlemler için kaynak verilerin sıralı bir şekilde teslim edilmesi gerekir. Sorgu ParallelEnumerable işleçleri gerektiğinde otomatik olarak sıralı moda geri döner. PlINQ, sıralı yürütme gerektiren kullanıcı tanımlı sorgu işleçleri ve kullanıcı temsilcileri için AsSequential yöntemini sağlar. kullandığınızda AsSequential, sorgudaki sonraki tüm işleçler yeniden çağrılana kadar AsParallel sırayla yürütülür. Daha fazla bilgi için bkz . Nasıl yapılır: Paralel ve Sıralı LINQ Sorgularını Birleştirme.

Sorgu Sonuçlarını Birleştirme Seçenekleri

PlINQ sorgusu paralel olarak yürütürse, her çalışan iş parçacığından elde ettiği sonuçlar bir foreach döngü (For Each Visual Basic'te) veya bir listeye veya diziye eklemek için ana iş parçacığına geri birleştirilmelidir. Bazı durumlarda, örneğin sonuçları daha hızlı bir şekilde üretmeye başlamak için belirli bir birleştirme işlemi türünü belirtmek yararlı olabilir. Bu amaçla PLINQ yöntemini ve numaralandırmasını ParallelMergeOptions desteklerWithMergeOptions. Daha fazla bilgi için bkz . PLINQ'ta Birleştirme Seçenekleri.

ForAll İşleci

Sıralı LINQ sorgularında, sorgu bir foreach (For Each Visual Basic'te) döngüsünde veya , veya ToDictionarygibi bir yöntem çağrılarak numaralandırılana kadar ToListToArray yürütme ertelenebilir. PLINQ'ta sorguyu yürütmek ve sonuçlar arasında yineleme yapmak için de kullanabilirsiniz foreach . Ancak, foreach kendisi paralel olarak çalışmaz ve bu nedenle, tüm paralel görevlerden gelen çıkışın döngünün çalıştığı iş parçacığıyla yeniden birleştirilmesini gerektirir. PLINQ'ta, sorgu sonuçlarının son sıralamasını korumanız gerektiğinde ve sonuçları seri bir şekilde işlerken (örneğin, her öğe için çağırdığınızdaConsole.WriteLine) kullanabilirsinizforeach. Sipariş koruması gerekli olmadığında ve sonuçların işlenmesi kendi kendine paralel hale getirilebildiğinde daha hızlı sorgu yürütme için PLINQ sorgusu yürütmek için yöntemini kullanın ForAll . ForAll bu son birleştirme adımlarını gerçekleştirmez. Aşağıdaki kod örneğinde yönteminin nasıl kullanılacağı gösterilmektedir ForAll . System.Collections.Concurrent.ConcurrentBag<T> burada kullanılır çünkü hiçbir öğeyi kaldırmaya çalışmadan eşzamanlı olarak ekleyen birden çok iş parçacığı için iyileştirilmiştir.

var nums = Enumerable.Range(10, 10000);
var query =
    from num in nums.AsParallel()
    where num % 10 == 0
    select num;

// Process the results as each thread completes
// and add them to a System.Collections.Concurrent.ConcurrentBag(Of Int)
// which can safely accept concurrent add operations
query.ForAll(e => concurrentBag.Add(Compute(e)));
Dim nums = Enumerable.Range(10, 10000)
Dim query = From num In nums.AsParallel()
            Where num Mod 10 = 0
            Select num

' Process the results as each thread completes
' and add them to a System.Collections.Concurrent.ConcurrentBag(Of Int)
' which can safely accept concurrent add operations
query.ForAll(Sub(e) concurrentBag.Add(Compute(e)))

Aşağıdaki çizimde sorgu yürütmeyle ilgili ile ForAll arasındaki foreach fark gösterilmektedir.

ForAll vs. ForEach

İptal

PLINQ, .NET'teki iptal türleriyle tümleşiktir. (Daha fazla bilgi için bkz. Yönetilen İş Parçacıklarında İptal.) Bu nedenle, sıralı LINQ to Objects sorgularının aksine PLINQ sorguları iptal edilebilir. İptal edilebilir bir PLINQ sorgusu oluşturmak için sorgudaki işlecini kullanın WithCancellation ve bağımsız değişken olarak bir CancellationToken örnek sağlayın. Belirteç üzerindeki IsCancellationRequested özellik true olarak ayarlandığında, PLINQ bunu fark eder, tüm iş parçacıklarında işlemeyi durdurur ve bir OperationCanceledExceptionoluşturur.

PLINQ sorgusu, iptal belirteci ayarlandıktan sonra bazı öğeleri işlemeye devam edebilir.

Daha fazla yanıt vermek için, uzun süre çalışan kullanıcı temsilcilerindeki iptal isteklerine de yanıt vekleyebilirsiniz. Daha fazla bilgi için bkz . Nasıl yapılır: PLINQ Sorgusunu İptal Etme.

Özel durumlar

PLINQ sorgusu yürütürken, aynı anda farklı iş parçacıklarından birden çok özel durum oluşturulabilir. Ayrıca, özel durumu işlemek için kod, özel durum oluşturan koddan farklı bir iş parçacığında olabilir. PLINQ, bir sorgu tarafından oluşan tüm özel durumları kapsüllemek ve bu özel durumları çağıran iş parçacığına geri sıralamak için türünü kullanır AggregateException . Çağıran iş parçacığında yalnızca bir try-catch bloğu gereklidir. Bununla birlikte, içinde kapsüllenen AggregateException tüm özel durumları yineleyebilir ve güvenli bir şekilde kurtarabileceğiniz herhangi bir özel durumu yakalayabilirsiniz. Nadir durumlarda, içinde AggregateExceptionsarmalanmayan bazı özel durumlar oluşturulabilir ve ThreadAbortExceptionbunlar da sarmalanmaz.

Özel durumların birleştirilmiş iş parçacığına geri dönmesine izin verildiğinde, bir sorgu özel durum oluşturduktan sonra bazı öğeleri işlemeye devam edebilir.

Daha fazla bilgi için bkz . Nasıl yapılır: PLINQ Sorgusunda Özel Durumları İşleme.

Özel Bölümleyiciler

Bazı durumlarda, kaynak verilerin bazı özelliklerinden yararlanan özel bir bölümleyici yazarak sorgu performansını geliştirebilirsiniz. Sorguda, özel bölümleyici sorgulanan numaralandırılabilir nesnedir.

int[] arr = new int[9999];
Partitioner<int> partitioner = new MyArrayPartitioner<int>(arr);
var query = partitioner.AsParallel().Select(SomeFunction);
Dim arr(10000) As Integer
Dim partitioner As Partitioner(Of Integer) = New MyArrayPartitioner(Of Integer)(arr)
Dim query = partitioner.AsParallel().Select(Function(x) SomeFunction(x))

PLINQ sabit sayıda bölümü destekler (ancak veriler yük dengeleme için çalışma zamanında bu bölümlere dinamik olarak yeniden atanabilir.). For ve ForEach yalnızca dinamik bölümleme desteği sağlar. Bu, çalışma zamanında bölüm sayısının değiştiği anlamına gelir. Daha fazla bilgi için bkz . PLINQ ve TPL için Özel Bölümleyiciler.

PLINQ Performansını Ölçme

Çoğu durumda sorgu paralelleştirilebilir, ancak paralel sorguyu ayarlamanın getirdiği ek yük, elde edilen performans avantajından daha fazladır. Sorgu çok fazla hesaplama gerçekleştirmezse veya veri kaynağı küçükse, PLINQ sorgusu sıralı LINQ to Objects sorgusundan daha yavaş olabilir. Çeşitli sorguların performansını karşılaştırmak, işleme performans sorunlarını bulmak ve sorgunuzun paralel mi yoksa sırayla mı çalıştığını belirlemek için Visual Studio Team Server'daki Paralel Performans Analizi kullanabilirsiniz. Daha fazla bilgi için bkz . Eşzamanlılık Görselleştiricisi ve Nasıl yapılır: PLINQ Sorgu Performansını Ölçme.

Ayrıca bkz.