5.6. Parallel LINQ

PLINQ is the parallelized version of LINQ to Objects and supports all existing LINQ operators and functionality with a few new options for fine-grained control of parallelization functionality.

At the time of writing, LINQ to SQL and LINQ to Entities will not benefit from parallelization because in these cases the query is executed on the database or the provider, so .NET cannot parallelize it.

5.6.1. Why Not Parallelize All LINQ Queries Automatically?

Parallelizing LINQ queries automatically is potentially the ultimate goal for LINQ, but it can introduce some issues (particularly around ordering), so at present you have to opt in to the parallel model.

A WORD OF WARNING

When using PLINQ, it is important to ensure that your query does not modify the result set because this might have unforeseen effects if values are utilized later in the query. PLINQ will try to work out how best to process the query (including not running it in parallel at all), but do you really want to take the chance of weird, scary, and hard-to-reproduce bugs?


5.6.2. Hello PLINQ

This example iterates through all the objects in the stock list, calls an external service, and processes the result.

Writing such a query in traditional LINQ might look something like this:

var query = from s in Stocks
                  let result = StockService.CallService(s)
                  select result;

To run the same query in parallel, simply use the .AsParallel() extension method to the Stocks object:

var query = from s in Stocks.AsParallel()
                  let result = StockService.CallService(s)
                  select result;

It really is as easy as that (well, almost).

5.6.3. Ordering Results

As items are processed in parallel you should not write code that is dependent on tasks being processed in a specific order. To order the results of your queries, use the AsOrdered() method to tell .NET to buffer the results before sorting them. This will slow the query down slightly because PLINQ now has to do additional work to preserve the ordering and merge the output:

var query = from s in Stocks.AsParallel().AsOrdered()
                  orderby s.Company
                  let company = s.Company
                  let result = StockService.CallService(s)

Note that the AsUnordered() operator can be used to tell PLINQ that you no longer care about ordering items.

5.6.4. ForAll Operator()

If, however, the order of your results is not important, you should use the ForAll() operator, which avoids merging the results set and executes more quickly:

query.ForAll(result => Console.WriteLine(result));

Query performance can also be further increased by using the orderby clause in your LINQ query when combined with a filtering operation such as where because the ordering will then be applied only to the filtered results.


5.6.5. AsSequential()

The AsSequential() method forces PLINQ to process all operations sequentially, which can sometimes be required when you are working with user-defined query methods:

var query = from s in Stocks.AsParallel().AsSequential()
                  let result = StockService.CallService(s)
                  select result;

5.6.6. WithMergeOptions

The WithMergeOptions operator allows you to tell PLINQ how you want results to be merged when processing is complete. PLINQ is not guaranteed to do this, though. WithMergeOptions operates in three modes:

  • NotBuffered: Results are returned sooner, but more slowly overall.

  • FullyBuffered: This option is the quickest, but results are returned the most slowly.

  • AutoBuffered: This chunks returned items together, offering a middle ground between the other two options.

5.6.7. PLINQ Performance

Sometimes the overhead of parallelizing a query can actually make it perform more slowly than if it were run sequentially, so be sure to measure your queries' performance. LINQ queries are not actually executed until you enumerate through them (deferred execution), so measuring performance can be slightly harder. Thus, if you want to measure the performance, be sure to iterate through the data in the result set or call a method such as ToList().

Visual Studio Premium edition and above also contain a parallel performance analyzer, which allows you to compare the performance of queries.


5.6.8. Cancelling a PLINQ Query

You can cancel a PLINQ query by passing in a CancellationTokenSource, which is discussed very shortly, into the WithCancellation()method.

5.6.9. Exceptions and PLINQ

When a query is run in parallel, exceptions can occur in multiple threads. PLINQ aggregates these exceptions into an AggregateException class and returns them back to the caller. You can then iterate through each individual exception.

If you run the following example, you need to modify a setting in the IDE to see it working. To do this, go to Tools Options Debugging General and uncheck the "Enable just my code option" or run in Release mode.

//select stock that doesnt exist
var query =  from s in Stocks.AsParallel()
let result = StockService.CallService(Stocks[11])
select result;

try
{
   query.ForAll(result=>Console.WriteLine(result.ToString()));
}
catch (AggregateException e)
{
    foreach (var ex in e.InnerExceptions)
    {
        Console.WriteLine(ex.Message);
    }
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.54.245