Benutzerdefinierte Partitionierer für PLINQ und TPLCustom Partitioners for PLINQ and TPL

Einer der wesentlichen Schritte, um einen Vorgang für eine Datenquelle zu parallelisieren, ist das Partitionieren der Quelle in mehrere Abschnitte, auf die mehrere Threads gleichzeitig zugreifen können.To parallelize an operation on a data source, one of the essential steps is to partition the source into multiple sections that can be accessed concurrently by multiple threads. PLINQ und die Task Parallel Library (TPL) stellen standardmäßige Partitionierer bereit, die transparent arbeiten, wenn Sie eine parallele Abfrage oder ForEach-Schleife schreiben.PLINQ and the Task Parallel Library (TPL) provide default partitioners that work transparently when you write a parallel query or ForEach loop. Für erweiterte Szenarien können Sie Ihren eigenen Partitionierer einbeziehen.For more advanced scenarios, you can plug in your own partitioner.

Arten der PartitionierungKinds of Partitioning

Es gibt viele Möglichkeiten, eine Datenquelle zu partitionieren.There are many ways to partition a data source. Bei den effizientesten Ansätzen wird die Quelle nicht physisch in mehrere Untersequenzen unterteilt, sondern mehrere Threads kooperieren beim Verarbeiten der ursprünglichen Quellsequenz.In the most efficient approaches, multiple threads cooperate to process the original source sequence, rather than physically separating the source into multiple subsequences. Für Arrays und andere indizierte Quellen wie IList-Sammlungen, bei denen die Länge im Voraus bekannt ist, ist die Bereichspartitionierung die einfachste Art der Partitionierung.For arrays and other indexed sources such as IList collections where the length is known in advance, range partitioning is the simplest kind of partitioning. Jeder Thread empfängt eindeutige Indizes für Anfang und Ende, sodass er seinen Bereich der Quelle verarbeiten kann, ohne einen anderen Thread zu überschreiben oder selbst überschrieben zu werden.Every thread receives unique beginning and ending indexes, so that it can process its range of the source without overwriting or being overwritten by any other thread. Der einzige Mehraufwand bei der Bereichspartitionierung ist die ursprüngliche Erstellung von Bereichen; danach ist keine zusätzliche Synchronisierung erforderlich.The only overhead involved in range partitioning is the initial work of creating the ranges; no additional synchronization is required after that. Aus diesem Grund liefert sie gute Leistung, solange die Arbeitsauslastung gleichmäßig verteilt wird.Therefore, it can provide good performance as long as the workload is divided evenly. Ein Nachteil der Bereichspartitionierung ist, dass ein Thread, der seine Arbeit frühzeitig beendet, den anderen Threads keine Arbeit abnehmen kann.A disadvantage of range partitioning is that if one thread finishes early, it cannot help the other threads finish their work.

Für verknüpfte Listen oder andere Sammlungen, deren Länge nicht bekannt ist, können Sie die Blockpartitionierung verwenden.For linked lists or other collections whose length is not known, you can use chunk partitioning. Bei der Blockpartitionierung nutzt jeder Thread oder jede Aufgabe in einer parallelen Schleife oder Abfrage eine Anzahl von Quellelementen in einem Block, verarbeitet sie und kehrt dann zurück, um zusätzliche Elemente abzurufen.In chunk partitioning, every thread or task in a parallel loop or query consumes some number of source elements in one chunk, processes them, and then comes back to retrieve additional elements. Mit dem Partitionierer wird sichergestellt, dass alle Elemente verteilt werden und keine Duplikate vorhanden sind.The partitioner ensures that all elements are distributed and that there are no duplicates. Ein Block kann eine beliebige Größe haben.A chunk may be any size. Der in Gewusst wie: Implementieren von dynamischen Partitionen gezeigte Partitionierer erstellt z.B. Blöcke, die nur ein Element enthalten.For example, the partitioner that is demonstrated in How to: Implement Dynamic Partitions creates chunks that contain just one element. Solange die Blöcke nicht zu groß sind, bewirkt diese Art der Partitionierung auch grundsätzlich einen Lastenausgleich, da die Zuweisung von Elementen zu Threads nicht vorgegeben ist.As long as the chunks are not too large, this kind of partitioning is inherently load-balancing because the assignment of elements to threads is not pre-determined. Allerdings macht sich der Synchronisierungsmehraufwand für den Partitionierer immer dann bemerkbar, wenn der Thread einen anderen Block abrufen muss.However, the partitioner does incur the synchronization overhead each time the thread needs to get another chunk. Das Ausmaß der Synchronisierung ist in diesen Fällen umgekehrt proportional zur Größe der Blöcke.The amount of synchronization incurred in these cases is inversely proportional to the size of the chunks.

Im Allgemeinen ist die Bereichspartitionierung nur schneller, wenn die Ausführungszeit des Delegaten kurz bis mäßig ist, die Quelle über eine große Anzahl von Elementen verfügt und die gesamte Arbeit in jeder Partition ungefähr gleich ist.In general, range partitioning is only faster when the execution time of the delegate is small to moderate, and the source has a large number of elements, and the total work of each partition is roughly equivalent. Die Blockpartitionierung ist daher im Allgemeinen in den meisten Fällen schneller.Chunk partitioning is therefore generally faster in most cases. Bei Datenquellen mit einer kleinen Anzahl von Elementen oder längeren Ausführungszeiten für den Delegaten sind Leistung des Blocks und Bereichspartitionierung ungefähr gleich.On sources with a small number of elements or longer execution times for the delegate, then the performance of chunk and range partitioning is about equal.

Die TPL-Partitionierer unterstützen auch eine dynamische Anzahl von Partitionen.The TPL partitioners also support a dynamic number of partitions. Dies bedeutet, dass sie Partitionen während des Betriebs erstellen können, z.B. wenn die ForEach-Schleife eine neue Aufgabe erzeugt.This means they can create partitions on-the-fly, for example, when the ForEach loop spawns a new task. Dieses Feature ermöglicht dem Partitionierer, zusammen mit der Schleife selbst zu skalieren.This feature enables the partitioner to scale together with the loop itself. Dynamische Partitionierer bewirken auch grundsätzlich einen Lastenausgleich.Dynamic partitioners are also inherently load-balancing. Wenn Sie einen benutzerdefinierten Partitionierer erstellen, müssen Sie die Nutzung der dynamischen Partitionierung von einer ForEach-Schleife aus unterstützen.When you create a custom partitioner, you must support dynamic partitioning to be consumable from a ForEach loop.

Konfigurieren von Lastenausgleichspartitionierern für PLINQConfiguring Load Balancing Partitioners for PLINQ

Mit einigen Überladungen der Partitioner.Create-Methode können Sie einen Partitionierer für ein Array oder eine IList-Datenquelle erstellen und festlegen, ob er versuchen soll, die Arbeitsauslastung zwischen den Threads auszugleichen.Some overloads of the Partitioner.Create method let you create a partitioner for an array or IList source and specify whether it should attempt to balance the workload among the threads. Wenn der Partitionierer für den Lastenausgleich konfiguriert ist, wird die Blockpartitionierung verwendet, und die Elemente werden bei Anforderung jeder Partition in kleinen Blöcken übergeben.When the partitioner is configured to load-balance, chunk partitioning is used, and the elements are handed off to each partition in small chunks as they are requested. Dieser Ansatz stellt sicher, dass alle Partitionen über zu verarbeitende Elemente verfügen, bis die gesamte Schleife oder Abfrage abgeschlossen ist.This approach helps ensure that all partitions have elements to process until the entire loop or query is completed. Eine weitere Überladung kann zur Lastenausgleichspartitionierung einer beliebigen IEnumerable-Quelle dienen.An additional overload can be used to provide load-balancing partitioning of any IEnumerable source.

Lastenausgleich erfordert im Allgemeinen, dass die Partitionen relativ häufig Elemente vom Partitionierer anfordern.In general, load balancing requires the partitions to request elements relatively frequently from the partitioner. Dagegen kann ein Partitionierer, der statische Partitionierung durchführt, mithilfe der Bereichs- oder Blockpartitionierung alle Elemente jedem Partitionierer gleichzeitig zuweisen.By contrast, a partitioner that does static partitioning can assign the elements to each partitioner all at once by using either range or chunk partitioning. Dies erfordert weniger Mehraufwand als der Lastenausgleich, aber die Ausführung dauert möglicherweise länger, wenn ein Thread am Ende deutlich mehr Arbeit bewältigen muss als die anderen.This requires less overhead than load balancing, but it might take longer to execute if one thread ends up with significantly more work than the others. Wenn PLINQ eine IList oder ein Array übergeben wird, verwendet PLINQ standardmäßig immer die Bereichspartitionierung ohne Lastenausgleich.By default when it is passed an IList or an array, PLINQ always uses range partitioning without load balancing. Um den Lastenausgleich für PLINQ zu aktivieren, verwenden Sie die Partitioner.Create-Methode, wie im folgenden Beispiel gezeigt.To enable load balancing for PLINQ, use the Partitioner.Create method, as shown in the following example.

// Static partitioning requires indexable source. Load balancing
// can use any IEnumerable.
var nums = Enumerable.Range(0, 100000000).ToArray();

// Create a load-balancing partitioner. Or specify false for static partitioning.
Partitioner<int> customPartitioner = Partitioner.Create(nums, true);

// The partitioner is the query's data source.
var q = from x in customPartitioner.AsParallel()
        select x * Math.PI;

q.ForAll((x) =>
{
    ProcessData(x);
});
' Static number of partitions requires indexable source.
Dim nums = Enumerable.Range(0, 100000000).ToArray()

' Create a load-balancing partitioner. Or specify false For  Shared partitioning.
Dim customPartitioner = Partitioner.Create(nums, True)

' The partitioner is the query's data source.
Dim q = From x In customPartitioner.AsParallel()
        Select x * Math.PI

q.ForAll(Sub(x) ProcessData(x))

Ob in einem gegebenen Szenario der Lastenausgleich verwendet werden sollte, bestimmen Sie am besten, indem Sie durch Experimentieren und Messen feststellen, wie lange die Ausführung von Vorgängen unter repräsentativen Lasten und Computerkonfigurationen dauert.The best way to determine whether to use load balancing in any given scenario is to experiment and measure how long it takes operations to complete under representative loads and computer configurations. Die statische Partitionierung könnte z.B. möglicherweise auf einem Mehrkerncomputer mit nur wenigen Kernen für erhebliche Beschleunigung sorgen, doch bei Computern mit relativ vielen Kernen zu Verlangsamungen führen.For example, static partitioning might provide significant speedup on a multi-core computer that has only a few cores, but it might result in slowdowns on computers that have relatively many cores.

In der folgenden Tabelle sind die verfügbaren Optionen der Create-Methode aufgelistet.The following table lists the available overloads of the Create method. Diese Partitionierer sind nicht auf die Verwendung mit PLINQ oder Task beschränkt.These partitioners are not limited to use only with PLINQ or Task. Sie können auch mit jedem benutzerdefinierten parallelen Konstrukt verwendet werden.They can also be used with any custom parallel construct.

ÜberladungOverload Verwendet LastenausgleichUses load balancing
Create<TSource>(IEnumerable<TSource>) AlwaysAlways
Create<TSource>(TSource[], Boolean) Wenn das boolesche Argument als „true“ angegeben wirdWhen the Boolean argument is specified as true
Create<TSource>(IList<TSource>, Boolean) Wenn das boolesche Argument als „true“ angegeben wirdWhen the Boolean argument is specified as true
Create(Int32, Int32) NieNever
Create(Int32, Int32, Int32) NieNever
Create(Int64, Int64) NieNever
Create(Int64, Int64, Int64) NieNever

Konfigurieren von statischen Bereichspartitionierern für Parallel.ForEachConfiguring Static Range Partitioners for Parallel.ForEach

In einer For-Schleife wird der Inhalt der Schleife der Methode als Delegat bereitgestellt.In a For loop, the body of the loop is provided to the method as a delegate. Der Aufwand für den Aufruf dieses Delegaten ist mit dem des Aufrufs einer virtuellen Methode identisch.The cost of invoking that delegate is about the same as a virtual method call. In einigen Szenarien könnte der Inhalt einer parallelen Schleife so klein sein, dass der Aufwand des Delegatenaufrufs in jeder Schleifeniteration signifikant wird.In some scenarios, the body of a parallel loop might be small enough that the cost of the delegate invocation on each loop iteration becomes significant. In solchen Situationen können Sie mithilfe einer der Create-Überladungen eine IEnumerable<T>-Schnittstelle von Bereichspartitionen über die Quellelemente erstellen.In such situations, you can use one of the Create overloads to create an IEnumerable<T> of range partitions over the source elements. Dann übergeben Sie diese Sammlung von Bereichen an eine ForEach-Methode, deren Inhalt aus einer regulären for-Schleife besteht.Then, you can pass this collection of ranges to a ForEach method whose body consists of a regular for loop. Dieser Ansatz hat den Vorteil, dass der Aufwand für den Delegatenaufruf nur einmal pro Bereich und nicht einmal pro Element anfällt.The benefit of this approach is that the delegate invocation cost is incurred only once per range, rather than once per element. Das Grundmuster wird im folgenden Beispiel veranschaulicht.The following example demonstrates the basic pattern.

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {

        // Source must be array or IList.
        var source = Enumerable.Range(0, 100000).ToArray();

        // Partition the entire source array.
        var rangePartitioner = Partitioner.Create(0, source.Length);

        double[] results = new double[source.Length];

        // Loop over the partitions in parallel.
        Parallel.ForEach(rangePartitioner, (range, loopState) =>
        {
            // Loop over each range element without a delegate invocation.
            for (int i = range.Item1; i < range.Item2; i++)
            {
                results[i] = source[i] * Math.PI;
            }
        });

        Console.WriteLine("Operation complete. Print results? y/n");
        char input = Console.ReadKey().KeyChar;
        if (input == 'y' || input == 'Y')
        {
            foreach(double d in results)
            {
                Console.Write("{0} ", d);
            }
        }
    }
}
Imports System.Threading.Tasks
Imports System.Collections.Concurrent

Module PartitionDemo

    Sub Main()
        ' Source must be array or IList.
        Dim source = Enumerable.Range(0, 100000).ToArray()

        ' Partition the entire source array. 
        ' Let the partitioner size the ranges.
        Dim rangePartitioner = Partitioner.Create(0, source.Length)

        Dim results(source.Length - 1) As Double

        ' Loop over the partitions in parallel. The Sub is invoked
        ' once per partition.
        Parallel.ForEach(rangePartitioner, Sub(range, loopState)

                                               ' Loop over each range element without a delegate invocation.
                                               For i As Integer = range.Item1 To range.Item2 - 1
                                                   results(i) = source(i) * Math.PI
                                               Next
                                           End Sub)
        Console.WriteLine("Operation complete. Print results? y/n")
        Dim input As Char = Console.ReadKey().KeyChar
        If input = "y"c Or input = "Y"c Then
            For Each d As Double In results
                Console.Write("{0} ", d)
            Next
        End If

    End Sub
End Module

Jeder Thread in der Schleife empfängt ein eigenes Tuple<T1,T2>, das Start- und Endindexwerte im angegebenen untergeordneten Bereich enthält.Every thread in the loop receives its own Tuple<T1,T2> that contains the starting and ending index values in the specified sub-range. Die innere for-Schleife verwendet die fromInclusive- und toExclusive-Werte für einen direkten Schleifendurchlauf über das Array oder IList.The inner for loop uses the fromInclusive and toExclusive values to loop over the array or the IList directly.

Mit einer der Create-Überladungen können Sie Größe und Anzahl der Partitionen angeben.One of the Create overloads lets you specify the size of the partitions, and the number of partitions. Diese Überladung kann in Szenarien verwendet, wo die Arbeit pro Element so gering ist, dass auch ein einziger Aufruf einer virtuellen Methode pro Element eine merkliche Auswirkung auf die Leistung hat.This overload can be used in scenarios where the work per element is so low that even one virtual method call per element has a noticeable impact on performance.

Benutzerdefinierte PartitioniererCustom Partitioners

In einigen Szenarien kann es empfehlenswert oder sogar erforderlich sein, dass Sie Ihren eigenen Partitionierer implementieren.In some scenarios, it might be worthwhile or even required to implement your own partitioner. Beispielsweise könnten Sie über eine benutzerdefinierte Auflistungsklasse verfügen, die Sie basierend auf Ihren Kenntnissen der internen Struktur der Klasse effizienter partitionieren können als der Standardpartitionierer.For example, you might have a custom collection class that you can partition more efficiently than the default partitioners can, based on your knowledge of the internal structure of the class. Vielleicht möchten Sie auch Bereichspartitionen mit unterschiedlichen Größen basierend auf Ihrer Kenntnis der Dauer der Verarbeitung von Elementen an verschiedenen Positionen in der Quellsammlung erstellen.Or, you may want to create range partitions of varying sizes based on your knowledge of how long it will take to process elements at different locations in the source collection.

Leiten Sie zum Erstellen eines einfachen benutzerdefinierten Partitionierers eine Klasse von System.Collections.Concurrent.Partitioner<TSource> ab, und überschreiben Sie die virtuellen Methoden, wie in der folgenden Tabelle beschrieben.To create a basic custom partitioner, derive a class from System.Collections.Concurrent.Partitioner<TSource> and override the virtual methods, as described in the following table.

GetPartitions Diese Methode wird einmal vom Hauptthread aufgerufen und gibt eine IList(IEnumerator(TSource)) zurück.This method is called once by the main thread and returns an IList(IEnumerator(TSource)). Jeder Arbeitsthread in der Schleife oder Abfrage kann GetEnumerator in der Liste zum Abrufen eines IEnumerator<T> über eine unterschiedliche Partition aufrufen.Each worker thread in the loop or query can call GetEnumerator on the list to retrieve a IEnumerator<T> over a distinct partition.
SupportsDynamicPartitions Geben Sie true zurück, wenn Sie GetDynamicPartitions implementieren, andernfalls false.Return true if you implement GetDynamicPartitions, otherwise, false.
GetDynamicPartitions Wenn SupportsDynamicPartitionstrue ist, kann diese Methode optional anstelle von GetPartitions aufgerufen werden.If SupportsDynamicPartitions is true, this method can optionally be called instead of GetPartitions.

Wenn die Ergebnisse sortierbar sein müssen, oder Sie indizierten Zugriff auf die Elemente benötigen, leiten Sie von System.Collections.Concurrent.OrderablePartitioner<TSource> ab, und überschreiben Sie dessen virtuelle Methoden wie in der folgenden Tabelle beschrieben.If the results must be sortable or you require indexed access into the elements, then derive from System.Collections.Concurrent.OrderablePartitioner<TSource> and override its virtual methods as described in the following table.

GetPartitions Diese Methode wird einmal vom Hauptthread aufgerufen und gibt eine IList(IEnumerator(TSource)) zurück.This method is called once by the main thread and returns an IList(IEnumerator(TSource)). Jeder Arbeitsthread in der Schleife oder Abfrage kann GetEnumerator in der Liste zum Abrufen eines IEnumerator<T> über eine unterschiedliche Partition aufrufen.Each worker thread in the loop or query can call GetEnumerator on the list to retrieve a IEnumerator<T> over a distinct partition.
SupportsDynamicPartitions Geben Sie true zurück, wenn Sie GetDynamicPartitions implementieren; andernfalls „false“.Return true if you implement GetDynamicPartitions; otherwise, false.
GetDynamicPartitions In der Regel wird hiermit GetOrderableDynamicPartitions aufgerufen.Typically, this just calls GetOrderableDynamicPartitions.
GetOrderableDynamicPartitions Wenn SupportsDynamicPartitionstrue ist, kann diese Methode optional anstelle von GetPartitions aufgerufen werden.If SupportsDynamicPartitions is true, this method can optionally be called instead of GetPartitions.

Die folgende Tabelle enthält weitere Details zur Implementierung der OrderablePartitioner<TSource>-Klasse durch die drei Arten von Lastenausgleichspartitionierern.The following table provides additional details about how the three kinds of load-balancing partitioners implement the OrderablePartitioner<TSource> class.

Methode/EigenschaftMethod/Property IList/Array ohne LastenausgleichIList / Array without Load Balancing IList/Array mit LastenausgleichIList / Array with Load Balancing IEnumerableIEnumerable
GetOrderablePartitions Verwendet BereichspartitionierungUses range partitioning Verwendet optimierte Blockpartitionierung für Listen für die angegebene partitionCountUses chunk partitioning optimized for Lists for the partitionCount specified Verwendet Blockpartitionierung durch Erstellen einer statischen Anzahl von PartitionenUses chunk partitioning by creating a static number of partitions.
OrderablePartitioner<TSource>.GetOrderableDynamicPartitions Löst nicht unterstützte Ausnahme ausThrows not-supported exception Verwendet optimierte Blockpartitionierung für Listen und dynamische PartitionenUses chunk partitioning optimized for Lists and dynamic partitions Verwendet Blockpartitionierung durch Erstellen einer dynamischen Anzahl von Partitionen.Uses chunk partitioning by creating a dynamic number of partitions.
KeysOrderedInEachPartition Gibt true zurück.Returns true Gibt true zurück.Returns true Gibt true zurück.Returns true
KeysOrderedAcrossPartitions Gibt true zurück.Returns true Gibt false zurück.Returns false Gibt false zurück.Returns false
KeysNormalized Gibt true zurück.Returns true Gibt true zurück.Returns true Gibt true zurück.Returns true
SupportsDynamicPartitions Gibt false zurück.Returns false Gibt true zurück.Returns true Gibt true zurück.Returns true

Dynamische PartitionenDynamic Partitions

Wenn Sie beabsichtigen, den Partitionierer in einer ForEach-Methode zu verwenden, müssen Sie eine dynamische Anzahl von Partitionen zurückgeben können.If you intend the partitioner to be used in a ForEach method, you must be able to return a dynamic number of partitions. Dies bedeutet, dass der Partitionierer bei Bedarf jederzeit während der Schleifenausführung einen Enumerator für eine neue Partition bereitstellen kann.This means that the partitioner can supply an enumerator for a new partition on-demand at any time during loop execution. Im Wesentlichen wird immer dann, wenn die Schleife eine neue parallele Aufgabe hinzufügt, eine neue Partition für diese Aufgabe angefordert.Basically, whenever the loop adds a new parallel task, it requests a new partition for that task. Wenn die Daten sortierbar sein müssen, leiten Sie von System.Collections.Concurrent.OrderablePartitioner<TSource> ab, sodass jedem Element in jeder Partition ein eindeutiger Index zugewiesen wird.If you require the data to be orderable, then derive from System.Collections.Concurrent.OrderablePartitioner<TSource> so that each item in each partition is assigned a unique index.

Weitere Informationen und ein Beispiel finden Sie unter Gewusst wie: Implementieren von dynamischen Partitionen.For more information, and an example, see How to: Implement Dynamic Partitions.

Vertrag für PartitioniererContract for Partitioners

Wenn Sie einen benutzerdefinierten Partitionierer implementieren, befolgen Sie diese Richtlinien, um ordnungsgemäße Interaktion mit PLINQ und ForEach in der TPL sicherzustellen:When you implement a custom partitioner, follow these guidelines to help ensure correct interaction with PLINQ and ForEach in the TPL:

  • Wenn GetPartitions mit einem Argument von 0 (null) oder weniger für partitionsCount aufgerufen wird, soll ArgumentOutOfRangeException ausgelöst werden.If GetPartitions is called with an argument of zero or less for partitionsCount, throw ArgumentOutOfRangeException. Obwohl PLINQ und TPL niemals eine partitionCount gleich 0 übergeben werden, sollten Sie dennoch einen Schutz vor dieser Möglichkeit einbauen.Although PLINQ and TPL will never pass in a partitionCount equal to 0, we nevertheless recommend that you guard against the possibility.

  • GetPartitions und GetOrderablePartitions sollten stets eine partitionsCount Anzahl von Partitionen zurückgeben.GetPartitions and GetOrderablePartitions should always return partitionsCount number of partitions. Wenn dem Partitionierer keine Daten mehr zur Verfügung stehen, sodass er nicht mehr so viele Partitionen wie angefordert erstellen kann, sollte die Methode für jede der verbleibenden Partitionen einen leeren Enumerator zurückgeben.If the partitioner runs out of data and cannot create as many partitions as requested, then the method should return an empty enumerator for each of the remaining partitions. Andernfalls löst sowohl PLINQ als auch TPL eine InvalidOperationException aus.Otherwise, both PLINQ and TPL will throw an InvalidOperationException.

  • GetPartitions, GetOrderablePartitions, GetDynamicPartitions und GetOrderableDynamicPartitions sollten niemals null (Nothing in Visual Basic) zurückgeben.GetPartitions, GetOrderablePartitions, GetDynamicPartitions, and GetOrderableDynamicPartitions should never return null (Nothing in Visual Basic). Wenn dies doch der Fall ist, lösen PLINQ/TPL eine InvalidOperationException aus.If they do, PLINQ / TPL will throw an InvalidOperationException.

  • Partitionen zurückgebende Methoden sollten immer Partitionen zurückgeben, die die Datenquelle vollständig und eindeutig aufzählen können.Methods that return partitions should always return partitions that can fully and uniquely enumerate the data source. Sofern nicht ausdrücklich vom Entwurf des Partitionierers gefordert, dürfen in der Datenquelle keine Duplikate oder übersprungenen Elemente enthalten sein.There should be no duplication in the data source or skipped items unless specifically required by the design of the partitioner. Wenn diese Regel nicht befolgt wird, kann die Ausgabereihenfolge durcheinander geraten.If this rule is not followed, then the output order may be scrambled.

  • Die folgenden booleschen Getter müssen immer genau die folgenden Werte zurückgeben, damit die Reihenfolge der Ausgabe nicht durcheinander gerät:The following Boolean getters must always accurately return the following values so that the output order is not scrambled:

    • KeysOrderedInEachPartition: Jede Partition gibt Elemente mit zunehmenden Schlüsselindizes zurück.KeysOrderedInEachPartition: Each partition returns elements with increasing key indices.

    • KeysOrderedAcrossPartitions: Für alle zurückgegebenen Partitionen sind die Schlüsselindizes in Partition i höher als diejenigen in Partition i-1.KeysOrderedAcrossPartitions: For all partitions that are returned, the key indices in partition i are higher than the key indices in partition i-1.

    • KeysNormalized: Alle Schlüsselindizes nehmen beginnend mit 0 (null) ohne Lücken monoton zu.KeysNormalized: All key indices are monotonically increasing without gaps, starting from zero.

  • Alle Indizes müssen eindeutig sein.All indices must be unique. Es darf keine doppelten Indizes geben.There may not be duplicate indices. Wenn diese Regel nicht befolgt wird, kann die Ausgabereihenfolge durcheinander geraten.If this rule is not followed, then the output order may be scrambled.

  • Alle Indizes dürfen nicht negativ sein.All indices must be nonnegative. Wenn diese Regel nicht befolgt wird, kann PLINQ/TPL Ausnahmen auslösen.If this rule is not followed, then PLINQ/TPL may throw exceptions.

Weitere InformationenSee also