Übersetzung vorschlagen
 
Andere Vorschläge:

progress indicator
Keine anderen Vorschläge
Per Mausklick bewerten und Feedback geben
MSDN
MSDN Library
.NET Entwicklung
.NET Framework 4
 Einführung in PLINQ
Alle reduzieren/Alle erweitern Alle reduzieren
Inhalt anzeigen:  Englisch mit deutscher ÜbersetzungInhalt anzeigen: Englisch mit deutscher Übersetzung
.NET Framework 4
Introduction to PLINQ

Language-Integrated Query (LINQ) was introduced in the .NET Framework version 3.0 It features a unified model for querying any System.Collections..::.IEnumerable or System.Collections.Generic..::.IEnumerable<(Of <(T>)>) data source in a type-safe manner. LINQ to Objects is the name for LINQ queries that are run against in-memory collections such as List<(Of <(T>)>) and arrays. This article assumes that you have a basic understand of LINQ. For more information, see Language-Integrated Query (LINQ).

Parallel LINQ (PLINQ) is a parallel implementation of the LINQ pattern. A PLINQ query in many ways resembles a non-parallel LINQ to Objects query. PLINQ queries, just like sequential LINQ queries, operate on any in-memory IEnumerable or IEnumerable<(Of <(T>)>) data source, and have deferred execution, which means they do not begin executing until the query is enumerated. The primary difference is that PLINQ attempts to make full use of all the processors on the system. It does this by partitioning the data source into segments, and then executing the query on each segment on separate worker threads in parallel on multiple processors. In many cases, parallel execution means that the query runs significantly faster.

Through parallel execution, PLINQ can achieve significant performance improvements over legacy code for certain kinds of queries, often just by adding the AsParallel query operation to the data source. However, parallelism can introduce its own complexities, and not all query operations run faster in PLINQ. In fact, parallelization actually slows down certain queries. Therefore, you should understand how issues such as ordering affect parallel queries. For more information, see Understanding Speedup in PLINQ.

NoteNote

This documentation uses lambda expressions to define delegates in PLINQ. If you are not familiar with lambda expressions in C# or Visual Basic, see Lambda Expressions in PLINQ and TPL.

The remainder of this article gives an overview of the main PLINQ classes, and discusses how to create PLINQ queries. Each section contains links to more detailed information and code examples.

The System.Linq..::.ParallelEnumerable class exposes almost all of PLINQ's functionality. It and the rest of the System.Linq namespace types are compiled into the System.Core.dll assembly. The default C# and Visual Basic projects in Visual Studio both reference the assembly and import the namespace.

ParallelEnumerable includes implementations of all the standard query operators that LINQ to Objects supports, although it does not attempt to parallelize each one. If you are not familiar with LINQ, see Introduction to LINQ.

In addition to the standard query operators, the ParallelEnumerable class contains a set of methods that enable behaviors specific to parallel execution. These PLINQ-specific methods are listed in the following table.

ParallelEnumerable Operator

Description

AsParallel

The entry point for PLINQ. Specifies that the rest of the query should be parallelized, if it is possible.

AsSequential<(Of <(TSource>)>)

Specifies that the rest of the query should be run sequentially, as a non-parallel LINQ query.

AsOrdered

Specifies that PLINQ should preserve the ordering of the source sequence for the rest of the query, or until the ordering is changed, for example by the use of an orderby (Order By in Vlsual Basic) clause.

AsUnordered<(Of <(TSource>)>)

Specifies that PLINQ for the rest of the query is not required to preserve the ordering of the source sequence.

WithCancellation<(Of <(TSource>)>)

Specifies that PLINQ should periodically monitor the state of the provided cancellation token and cancel execution if it is requested.

WithDegreeOfParallelism<(Of <(TSource>)>)

Specifies the maximum number of processors that PLINQ should use to parallelize the query.

WithMergeOptions<(Of <(TSource>)>)

Provides a hint about how PLINQ should, if it is possible, merge parallel results back into just one sequence on the consuming thread.

WithExecutionMode<(Of <(TSource>)>)

Specifies whether PLINQ should parallelize the query even when the default behavior would be to run it sequentially.

ForAll<(Of <(TSource>)>)

A multithreaded enumeration method that, unlike iterating over the results of the query, enables results to be processed in parallel without first merging back to the consumer thread.

Aggregate overload

An overload that is unique to PLINQ and enables intermediate aggregation over thread-local partitions, plus a final aggregation function to combine the results of all partitions.

When you write a query, opt in to PLINQ by invoking the ParallelEnumerable..::.AsParallel extension method on the data source, as shown in the following example.

Visual Basic
Dim source = Enumerable.Range(1, 10000)

' Opt in to PLINQ with AsParallel
Dim evenNums = From num In source.AsParallel()
               Where Compute(num) > 0
               Select num
C#
var source = Enumerable.Range(1, 10000);


// Opt-in to PLINQ with AsParallel
var evenNums = from num in source.AsParallel()
               where Compute(num) > 0
               select num;

The AsParallel extension method binds the subsequent query operators, in this case, where and select, to the System.Linq..::.ParallelEnumerable implementations.

By default, PLINQ is conservative. At run time, the PLINQ infrastructure analyzes the overall structure of the query. If the query is likely to yield speedups by parallelization, PLINQ partitions the source sequence into tasks that can be run concurrently. If it is not safe to parallelize a query, PLINQ just runs the query sequentially. If PLINQ has a choice between a potentially expensive parallel algorithm or an inexpensive sequential algorithm, it chooses the sequential algorithm by default. You can use the WithExecutionMode<(Of <(TSource>)>) method and the System.Linq..::.ParallelExecutionMode enumeration to instruct PLINQ to select the parallel algorithm. This is useful when you know by testing and measurement that a particular query executes faster in parallel. For more information, see How to: Specify the Execution Mode in PLINQ.

By default, PLINQ uses all of the processors on the host computer up to a maximum of 64. You can instruct PLINQ to use no more than a specified number of processors by using the WithDegreeOfParallelism<(Of <(TSource>)>) method. This is useful when you want to make sure that other processes running on the computer receive a certain amount of CPU time. The following snippet limits the query to utilizing a maximum of two processors.

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

In cases where a query is performing a significant amount of non-compute-bound work such as File I/O, it might be beneficial to specify a degree of parallelism greater than the number of cores on the machine.

In some queries, a query operator must produce results that preserve the ordering of the source sequence. PLINQ provides the AsOrdered operator for this purpose. AsOrdered is distinct from AsSequential<(Of <(TSource>)>). An AsOrdered sequence is still processed in parallel, but its results are buffered and sorted. Because order preservation typically involves extra work, an AsOrdered sequence might be processed more slowly than the default AsUnordered<(Of <(TSource>)>) sequence. Whether a particular ordered parallel operation is faster than a sequential version of the operation depends on many factors.

The following code example shows how to opt in to order preservation.

Visual Basic
        Dim evenNums = From num In numbers.AsParallel().AsOrdered()
                      Where num Mod 2 = 0
                      Select num


C#

            evenNums = from num in numbers.AsParallel().AsOrdered()
                       where num % 2 == 0
                       select num;


For more information, see Order Preservation in PLINQ.

Some operations require that the source data be delivered in a sequential manner. The ParallelEnumerable query operators revert to sequential mode automatically when it is required. For user-defined query operators and user delegates that require sequential execution, PLINQ provides the AsSequential<(Of <(TSource>)>) method. When you use AsSequential<(Of <(TSource>)>), all subsequent operators in the query are executed sequentially until AsParallel is called again. For more information, see How to: Combine Parallel and Sequential LINQ Queries.

When a PLINQ query executes in parallel, its results from each worker thread must be merged back onto the main thread for consumption by a foreach loop (For Each in Visual Basic), or insertion into a list or array. In some cases, it might be beneficial to specify a particular kind of merge operation, for example, to begin producing results more quickly. For this purpose, PLINQ supports the WithMergeOptions<(Of <(TSource>)>)  method, and the ParallelMergeOptions enumeration. For more information, see Merge Options in PLINQ.

In sequential LINQ queries, execution is deferred until the query is enumerated either in a foreach (For Each in Visual Basic) loop or by invoking a method such as ToList<(Of <(TSource>)>) , ToArray<(Of <(TSource>)>) , or ToDictionary. In PLINQ, you can also use foreach to execute the query and iterate through the results. However, foreach itself does not run in parallel, and therefore, it requires that the output from all parallel tasks be merged back into the thread on which the loop is running. In PLINQ, you can use foreach when you must preserve the final ordering of the query results, and also whenever you are processing the results in a serial manner, for example when you are calling Console.WriteLine for each element. For faster query execution when order preservation is not required and when the processing of the results can itself be parallelized, use the ForAll<(Of <(TSource>)>) method to execute a PLINQ query. ForAll<(Of <(TSource>)>) does not perform this final merge step. The following code example shows how to use the ForAll<(Of <(TSource>)>) method. System.Collections.Concurrent..::.ConcurrentBag<(Of <(T>)>) is used here because it is optimized for multiple threads adding concurrently without attempting to remove any items.

Visual Basic
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)))
C#

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)));

The following illustration shows the difference between foreach and ForAll<(Of <(TSource>)>) with regard to query execution.

ForAll vs. ForEach

PLINQ is integrated with the cancellation types in .NET Framework 4. (For more information, see Cancellation.) Therefore, unlike sequential LINQ to Objects queries, PLINQ queries can be canceled. To create a cancelable PLINQ query, use the WithCancellation<(Of <(TSource>)>) operator on the query and provide a CancellationToken instance as the argument. When the IsCancellationRequested property on the token is set to true, PLINQ will notice it, stop processing on all threads, and throw an OperationCanceledException.

It is possible that a PLINQ query might continue to process some elements after the cancellation token is set.

For greater responsiveness, you can also respond to cancellation requests in long-running user delegates. For more information, see How to: Cancel a PLINQ Query.

When a PLINQ query executes, multiple exceptions might be thrown from different threads simultaneously. Also, the code to handle the exception might be on a different thread than the code that threw the exception. PLINQ uses the AggregateException type to encapsulate all the exceptions that were thrown by a query, and marshal those exceptions back to the calling thread. On the calling thread, only one try-catch block is required. However, you can iterate through all of the exceptions that are encapsulated in the AggregateException and catch any that you can safely recover from. In rare cases, some exceptions may be thrown that are not wrapped in an AggregateException, and ThreadAbortExceptions are also not wrapped.

When exceptions are allowed to bubble up back to the joining thread, then it is possible that a query may continue to process some items after the exception is raised.

For more information, see How to: Handle Exceptions in a PLINQ Query.

In some cases, you can improve query performance by writing a custom partitioner that takes advantage of some characteristic of the source data. In the query, the custom partitioner itself is the enumerable object that is queried.

[Visual Basic]
Dim arr(10000) As Integer
Dim partitioner = New MyArrayPartitioner(Of Integer)(arr)
Dim query = partitioner.AsParallel().Select(Function(x) SomeFunction(x))
[C#]
int[] arr= ...;
Partitioner<int> partitioner = newMyArrayPartitioner<int>(arr);
var q = partitioner.AsParallel().Select(x => SomeFunction(x));

PLINQ supports a fixed number of partitions (although data may be dynamically reassigned to those partitions during run time for load balancing.). For and ForEach support only dynamic partitioning, which means that the number of partitions changes at run time. For more information, see Custom Partitioners for PLINQ and TPL.

In many cases, a query can be parallelized, but the overhead of setting up the parallel query outweighs the performance benefit gained. If a query does not perform much computation or if the data source is small, a PLINQ query may be slower than a sequential LINQ to Objects query. You can use the Parallel Performance Analyzer in Visual Studio Team Server to compare the performance of various queries, to locate processing bottlenecks, and to determine whether your query is running in parallel or sequentially. For more information, see Concurrency Visualizer and How to: Measure PLINQ Query Performance.

.NET Framework 4
Einführung in PLINQ

Language Integrated Query (LINQ) wurde in .NET Framework 3.0 eingeführt und bietet ein einheitliches Modell für die typsichere Abfrage beliebiger System.Collections..::.IEnumerable- oder System.Collections.Generic..::.IEnumerable<(Of <(T>)>)-Datenquellen. LINQ to Objects ist der Name für LINQ-Abfragen von Auflistungen im Arbeitsspeicher, z. B. List<(Of <(T>)>), und Arrays. Dieser Artikel setzt Grundkenntnisse über LINQ voraus. Weitere Informationen finden Sie unter LINQ (Language-Integrated Query, sprachintegrierte Abfrage).

Parallel LINQ (PLINQ) ist eine parallele Implementierung des LINQ-Musters. Eine PLINQ-Abfrage entspricht weitgehend einer nicht parallelen LINQ to Objects-Abfrage. PLINQ-Abfragen werden, ebenso wie sequenzielle LINQ-Abfragen, auf alle IEnumerable- oder IEnumerable<(Of <(T>)>)-Datenquellen im Arbeitsspeicher angewendet und weisen eine verzögerte Ausführung auf, d. h. sie werden erst ausgeführt, wenn die Abfrage aufgelistet wird. Der Hauptunterschied besteht darin, dass PLINQ versucht, alle Prozessoren im System vollständig auszuschöpfen. PLINQ partitioniert hierzu die Datenquelle in Segmente und führt dann die Abfrage für jedes Segment parallel in separaten Arbeitsthreads und auf mehreren Prozessoren aus. In vielen Fällen bedeutet eine parallele Ausführung, dass die Abfrage deutlich schneller ausgeführt wird.

Durch die parallele Ausführung kann PLINQ für bestimmte Abfragen eine erheblich höhere Leistung als Legacycode erzielen. Häufig muss hierzu lediglich der AsParallel-Abfragevorgang zur Datenquelle hinzugefügt werden. Die Parallelität kann jedoch eigene Komplexitäten mit sich bringen, und nicht alle Abfragevorgänge werden in PLINQ schneller ausgeführt. Im Gegenteil, manche Abfragen werden durch die Parallelisierung sogar verlangsamt. Sie sollten daher wissen, wie sich bestimmte Aspekte, z. B. Sortierung, auf parallele Abfragen auswirken. Weitere Informationen finden Sie unter Grundlagen zur Beschleunigung in PLINQ.

HinweisHinweis

In dieser Dokumentation werden Delegaten in PLINQ mithilfe von Lambda-Ausdrücken definiert. Falls Sie nicht mit der Verwendung von Lambda-Ausdrücken in C# oder Visual Basic vertraut sind, finden Sie entsprechende Informationen unter Lambda-Ausdrücke in PLINQ und TPL.

Der Rest dieses Artikels bietet eine Übersicht über die wichtigsten PLINQ-Klassen und erläutert, wie PLINQ-Abfragen erstellt werden. Jeder Abschnitt enthält Links zu ausführlicheren Informationen und Codebeispielen.

Die System.Linq..::.ParallelEnumerable-Klasse stellt nahezu die gesamte Funktionalität von PLINQ zur Verfügung. Diese Klasse sowie die restlichen Typen des System.Linq-Namespace werden in der Assembly "System.Core.dll" kompiliert. Die standardmäßigen C#- und Visual Basic-Projekte in Visual Studio verweisen auf die Assembly und importiert den Namespace.

ParallelEnumerable enthält Implementierungen aller Standardabfrageoperatoren, die von LINQ to Objects unterstützt werden, auch wenn nicht für jeden eine Parallelisierung angestrebt wird. Weitere Informationen zu LINQ finden Sie unter Einführung in LINQ.

Zusätzlich zu den Standardabfrageoperatoren enthält die ParallelEnumerable-Klasse einen Satz von Methoden, die spezielle Verhaltensweisen für die parallele Ausführung unterstützen. Diese PLINQ-spezifischen Methoden sind in der folgenden Tabelle aufgeführt.

ParallelEnumerable-Operator

Beschreibung

AsParallel

Der Einstiegspunkt für PLINQ. Gibt an, dass der Rest der Abfrage nach Möglichkeit parallelisiert werden soll.

AsSequential<(Of <(TSource>)>)

Gibt an, dass der Rest der Abfrage sequenziell, als nicht parallele LINQ-Abfrage ausgeführt werden soll.

AsOrdered

Gibt an, diese PLINQ die Reihenfolge der Quellsequenz im Rest der Abfrage oder bis zu einer Änderung der Reihenfolge, z. B. durch eine orderby-Klausel (Ordner By in Vlsual Basic), beibehalten soll.

AsUnordered<(Of <(TSource>)>)

Gibt an, dass PLINQ die Reihenfolge der Quellsequenz im Rest der Abfrage nicht beibehalten muss.

WithCancellation<(Of <(TSource>)>)

Gibt an, diese PLINQ den Zustand des bereitgestellten Abbruchtokens in regelmäßigen Abständen überwachen und die Ausführung auf Anforderung abbrechen soll.

WithDegreeOfParallelism<(Of <(TSource>)>)

Gibt die maximale Anzahl von Prozessoren an, die PLINQ zum Parallelisieren der Abfrage verwenden soll.

WithMergeOptions<(Of <(TSource>)>)

Gibt an, wie PLINQ parallele Ergebnisse im Consumerthread wieder in einer Sequenz zusammenführen soll, sofern dies möglich ist.

WithExecutionMode<(Of <(TSource>)>)

Gibt an, ob PLINQ die Abfrage parallelisieren soll, selbst wenn diese standardmäßig sequenziell ausgeführt würde.

ForAll<(Of <(TSource>)>)

Eine Multithreadenumerationsmethode, die, im Gegensatz zum Durchlaufen der Ergebnisse der Abfrage, eine parallele Verarbeitung der Ergebnisse ermöglicht, ohne dass diese zuvor im Consumerthread zusammengeführt werden.

Aggregate-Überladung

Eine spezielle Überladung für PLINQ, die eine Zwischenaggregation über lokale Threadpartitionen sowie eine abschließende Aggregationsfunktion zum Kombinieren der Ergebnisse aller Partitionen ermöglicht.

Beim Schreiben einer Abfrage entscheiden Sie sich für PLINQ, indem Sie in der Datenquelle die ParallelEnumerable..::.AsParallel-Erweiterungsmethode aufrufen, wie im folgenden Beispiel gezeigt.

Visual Basic
Dim source = Enumerable.Range(1, 10000)

' Opt in to PLINQ with AsParallel
Dim evenNums = From num In source.AsParallel()
               Where Compute(num) > 0
               Select num
C#
var source = Enumerable.Range(1, 10000);


// Opt-in to PLINQ with AsParallel
var evenNums = from num in source.AsParallel()
               where Compute(num) > 0
               select num;

Die AsParallel-Erweiterungsmethode bindet die nachfolgenden Abfrageoperatoren, in diesem Fall, where und select, an die System.Linq..::.ParallelEnumerable-Implementierungen.

Standardmäßig wird PLINQ konservativ ausgeführt. Die PLINQ-Infrastruktur analysiert zur Laufzeit die Gesamtstruktur der Abfrage. Wenn die Abfrage voraussichtlich durch eine Parallelisierung beschleunigt werden kann, partitioniert PLINQ die Quellsequenz in Aufgaben, die gleichzeitig ausgeführt werden können. Kann eine Abfrage nicht sicher parallelisiert werden, führt PLINQ die Abfrage sequenziell aus. Wenn PLINQ die Wahl zwischen einem potenziell rechenintensiven parallelen Algorithmus und einem einfachen sequenziellen Algorithmus hat, entscheidet es sich standardmäßig für den sequenziellen Algorithmus. Mit der WithExecutionMode<(Of <(TSource>)>)-Methode und der System.Linq..::.ParallelExecutionMode-Enumeration können Sie PLINQ anweisen, den parallelen Algorithmus auszuwählen. Dies ist sinnvoll, wenn Sie aufgrund von Tests und Messungen wissen, dass eine bestimmte Abfrage im parallelen Modus schneller ausgeführt wird. Weitere Informationen finden Sie unter Gewusst wie: Angeben des Ausführungsmodus in PLINQ.

Standardmäßig verwendet PLINQ alle Prozessoren des Hostcomputers bis zu einem Maximum von 64. Mit der WithDegreeOfParallelism<(Of <(TSource>)>)-Methode können Sie PLINQ anweisen, nicht mehr als eine angegebene Anzahl von Prozessoren zu verwenden. Sie können so sicherstellen, dass andere auf dem Computer ausgeführt Prozesse eine bestimmte Menge an CPU-Zeit erhalten. Der folgende Codeausschnitt beschränkt die Abfrage auf maximal zwei Prozessoren.

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

Wenn eine Abfrage eine große Mengen an nicht rechnergebundenen Aufgaben ausführt, z. B. Datei-E/A, kann es von Vorteil sein, einen höheren Grad an Parallelität als die auf dem Rechner verfügbare Anzahl von Kernen anzugeben.

Bei einigen Abfragen muss ein Abfrageoperator Ergebnisse erzeugen, die die Reihenfolge der Quellsequenz beibehalten. PLINQ stellt für diesen Zweck den AsOrdered-Operator bereit. AsOrdered unterscheidet sich von AsSequential<(Of <(TSource>)>). Eine AsOrdered-Sequenz wird weiterhin parallel ausgeführt, die Ergebnisse werden jedoch gepuffert und sortiert. Da die Beibehaltung der Reihenfolge in der Regel einen gewissen Mehraufwand bedeutet, wird eine AsOrdered-Sequenz ggf. langsamer verarbeitet als die standardmäßige AsUnordered<(Of <(TSource>)>)-Sequenz. Ob ein bestimmter geordneter paralleler Vorgang schneller ist als eine sequenzielle Version des Vorgangs, hängt von vielen Faktoren ab.

Im folgenden Codebeispiel wird gezeigt, wie Sie die Beibehaltung der Reihenfolge aktivieren.

Visual Basic
        Dim evenNums = From num In numbers.AsParallel().AsOrdered()
                      Where num Mod 2 = 0
                      Select num


C#

            evenNums = from num in numbers.AsParallel().AsOrdered()
                       where num % 2 == 0
                       select num;


Weitere Informationen finden Sie unter Beibehaltung der Reihenfolge in PLINQ.

Einige Vorgänge erfordern, dass die Quelldaten sequenziell übergeben werden. Die ParallelEnumerable-Abfrageoperatoren stellen automatisch den sequenziellen Modus wieder her, wenn dies erforderlich ist. Für benutzerdefinierte Abfrageoperatoren und Benutzerdelegaten, die eine sequenzielle Ausführung erfordern, stellt PLINQ die AsSequential<(Of <(TSource>)>)-Methode bereit. Bei Verwendung von AsSequential<(Of <(TSource>)>) werden alle nachfolgenden Operatoren in der Abfrage sequenziell ausgeführt, bis AsParallel erneut aufgerufen wird. Weitere Informationen finden Sie unter Gewusst wie: Kombinieren von parallelen und sequenziellen LINQ-Abfragen.

Wenn eine PLINQ-Abfrage parallel ausgeführt wird, müssen die Ergebnisse aller Arbeitsthreads wieder im Hauptthread zusammengeführt werden, damit sie in einerforeach-Schleife (For Each in Visual Basic) verwendet oder in eine Liste bzw. ein Array eingefügt werden können. In einigen Fällen ist es hilfreich, eine bestimmte Art von Zusammenführung anzugeben, z. B. um Ergebnisse schneller zu erzeugen. Zu diesem Zweck unterstützt PLINQ die WithMergeOptions<(Of <(TSource>)>)-Methode und die ParallelMergeOptions-Enumeration. Weitere Informationen finden Sie unter Zusammenführungsoptionen in PLINQ.

In sequenziellen LINQ-Abfragen wird die Ausführung verzögert, bis die Abfrage entweder in einer foreach-Schleife (For Each in Visual Basic) oder durch das Aufrufen einer Methode, z. B. ToList<(Of <(TSource>)>), ToArray<(Of <(TSource>)>) oder ToDictionary, aufgelistet wird. In PLINQ können Sie zudem foreach verwenden, um die Abfrage auszuführen und die Ergebnisse zu durchlaufen. foreach selbst wird jedoch nicht parallel ausgeführt, sodass die Ausgabe aller parallelen Aufgaben wieder in dem Thread, in dem die Schleife ausgeführt wird, zusammengeführt werden muss. In PLINQ können Sie foreach verwenden, wenn Sie die abschließende Reihenfolge der Abfrageergebnisse beibehalten möchten oder wenn Sie die Ergebnisse seriell verarbeiten, z. B. wenn Sie für jedes Element Console.WriteLine aufrufen. Wenn die Beibehaltung der Reihenfolge nicht erforderlich ist und die Verarbeitung der Ergebnisse selbst parallelisiert werden kann, verwenden Sie die ForAll<(Of <(TSource>)>)-Methode, um die Ausführung der PLINQ-Abfrage zu beschleunigen. ForAll<(Of <(TSource>)>) führt die abschließende Zusammenführung nicht aus. Im folgenden Codebeispiel wird die Verwendung der ForAll<(Of <(TSource>)>)-Methode veranschaulicht. System.Collections.Concurrent..::.ConcurrentBag<(Of <(T>)>) wird hier verwendet, da sie für mehrere Threads optimiert ist, die gleichzeitig Elemente hinzufügen, ohne Elemente zu entfernen.

Visual Basic
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)))
C#

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)));

Die folgende Abbildung zeigt den Unterschied zwischen foreach und ForAll<(Of <(TSource>)>) hinsichtlich der Abfrageausführung.

ForAll und ForEach

PLINQ ist in die Abbruchtypen in .NET Framework 4 integriert. (Weitere Informationen finden Sie unter Abbruch.) Daher können PLINQ-Abfragen im Gegensatz zu sequenziellen LINQ to Objects-Abfragen abgebrochen werden. Um eine abbrechbare PLINQ-Abfrage zu erstellen, verwenden Sie den WithCancellation<(Of <(TSource>)>)-Operator in der Abfrage, und stellen Sie eine CancellationToken-Instanz als Argument bereit. Wenn die IsCancellationRequested-Eigenschaft im Token auf true festgelegt ist, erkennt PLINQ dies. Die Verarbeitung wird in diesem Fall in allen Threads abgebrochen, und eine OperationCanceledException wird ausgelöst.

Es ist möglich, dass eine PLINQ-Abfrage nach dem Festlegen des Abbruchtokens weiterhin einige Elemente verarbeitet.

Um kürzere Reaktionszeiten zu erzielen, können Sie auch auf Abbruchanforderungen in Benutzerdelegaten mit langer Laufzeit reagieren. Weitere Informationen finden Sie unter Gewusst wie: Abbrechen einer PLINQ-Abfrage.

Bei der Ausführung einer PLINQ-Abfrage können mehrere Ausnahmen von verschiedenen Threads gleichzeitig ausgelöst werden. Zudem kann sich der Code für die Behandlung einer Ausnahme in einem anderen Thread befinden als der Code, der die Ausnahme ausgelöst hat. PLINQ kapselt alle Ausnahmen, die von einer Abfrage ausgelöst wurden, mithilfe des AggregateException-Typs und marshallt diese Ausnahmen zurück an den aufrufenden Thread. Im aufrufenden Thread ist nur ein try/catch-Block erforderlich. Sie können jedoch alle Ausnahmen durchlaufen, die in AggregateException gekapselt sind, und die Ausnahmen erfassen, die Sie sicher beheben können. In seltenen Fällen können Ausnahmen ausgelöst werden, die nicht in einer AggregateException eingeschlossen sind. ThreadAbortExceptions sind ebenfalls nicht eingeschlossen.

Wenn Ausnahmen mittels Bubbling wieder an den Verbindungsthread übergeben werden können, ist es möglich, dass eine Abfrage nach dem Auslösen der Ausnahme weiterhin einige Elemente verarbeitet.

Weitere Informationen finden Sie unter Gewusst wie: Behandeln von Ausnahmen in einer PLINQ-Abfrage.

Sie können die Abfrageleistung teilweise erhöhen, indem Sie einen benutzerdefinierten Partitionierer schreiben, der bestimmte Merkmale der Quelldaten nutzt. In der Abfrage ist der benutzerdefinierte Partitionierer das auflistbare Objekt, das abgefragt wird.

[Visual Basic]
Dim arr(10000) As Integer
Dim partitioner = New MyArrayPartitioner(Of Integer)(arr)
Dim query = partitioner.AsParallel().Select(Function(x) SomeFunction(x))
[C#]
int[] arr= ...;
Partitioner<int> partitioner = newMyArrayPartitioner<int>(arr);
var q = partitioner.AsParallel().Select(x => SomeFunction(x));

PLINQ unterstützt eine feste Anzahl von Partitionen (auch wenn die Daten während der Laufzeit diesen Partitionen dynamisch neu zugeordnet werden können, um einen Lastenausgleich zu gewährleisten). For und ForEach unterstützen nur die dynamische Partitionierung, d. h. die Anzahl der Partitionen ändert sich zur Laufzeit. Weitere Informationen finden Sie unter Benutzerdefinierte Partitionierer für PLINQ und TPL.

In vielen Fällen kann eine Abfrage parallelisiert werden, der mit dem Einrichten der parallelen Abfrage verbundene Mehraufwand überwiegt jedoch die erzielte Leistungssteigerung. Wenn eine Abfrage nur wenige Berechnung ausführt oder wenn die Datenquelle klein ist, ist eine PLINQ-Abfrage möglicherweise langsamer als ein sequenzielle LINQ to Objects-Abfrage. Mithilfe des Parallel Performance Analyzer in Visual Studio Team Server können Sie die Leistung verschiedener Abfragen vergleichen, Verarbeitungsengpässe suchen und bestimmen, ob die Abfrage parallel oder sequenziell ausgeführt wird. Weitere Informationen finden Sie unter Parallelitätsschnellansicht und Gewusst wie: Messen der Leistung von PLINQ-Abfragen.

Communityinhalt   Was ist Community Content?
Neuen Inhalt hinzufügen RSS  Anmerkungen
Processing
© 2012 Microsoft. Alle Rechte vorbehalten. Nutzungsbedingungen | Markenzeichen | Informationen zur Datensicherheit
Page view tracker