15.02.2015 Views

C# 4 and .NET 4

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

290 ❘ ChaPTer 11 lAnGuAGe inteGrAted Query<br />

returns ParallelQuery. Because of the returned type, the Where() method that is chosen by<br />

the compiler is ParallelEnumerable.Where() instead of Enumerable.Where(). In the following code, the<br />

Select() <strong>and</strong> Sum() methods are from ParallelEnumerable as well. In contrast to the implementation of<br />

the Enumerable class, with the ParallelEnumerable class the query is partitioned so that multiple threads<br />

can work on the query. The array can be split into multiple parts where different threads work on every part<br />

to filter the remaining items. After the partitioned work is completed, merging needs to take place to get the<br />

summary result of all parts.<br />

var sum = data.AsParallel().Where(x => x < 20).Select(x => x).Sum();<br />

Running this code starts the task manager so you can see that all CPUs of your system are busy. If you<br />

remove the AsParallel() method, multiple CPUs might not be used. Of course if you do not have multiple<br />

CPUs on your system, then don’t expect to see an improvement with the parallel version.<br />

Partitioners<br />

The AsParallel() method is an extension not only to the IEnumerable interface, but also to the<br />

Partitioner class. With this you can influence the partitions to be created.<br />

The Partitioner class is defined with the namespace System.Collections.Concurrent <strong>and</strong> has different<br />

variants. The Create() method accepts arrays or objects implementing IList. Depending on that, as<br />

well as on the parameter loadBalance that is of type Boolean <strong>and</strong> available with some overloads of the<br />

method, a different partitioner type is returned. For arrays, .<strong>NET</strong> 4 includes DynamicPartitionerFor<br />

Array <strong>and</strong> StaticPartitionerForArray that both derive from the abstract base<br />

class OrderablePartitioner.<br />

The code from the “Parallel Queries” section is changed to manually create a partitioner instead of relying<br />

on the default one:<br />

var sum = (from x in Partitioner.Create(data, true).AsParallel()<br />

where x < 20<br />

select x).Sum();<br />

You can also influence the parallelism by invoking the methods WithExecutionMode()<br />

<strong>and</strong> WithDegreeOfParallelism(). With WithExecutionMode() you can pass a value of<br />

ParallelExecutionMode that can be Default or ForceParallelism. By default, Parallel LINQ avoids<br />

parallelism with high overheads. With the method WithDegreeOfParallelism() you can pass an integer<br />

value to specify the maximum number of tasks that should run in parallel.<br />

Cancellation<br />

.<strong>NET</strong> 4 offers a st<strong>and</strong>ard way to cancel long-running tasks, <strong>and</strong> this is also true for Parallel LINQ.<br />

To cancel a long-running query, you can add the method WithCancellation() to the query<br />

<strong>and</strong> pass a CancellationToken to the parameter. The CancellationToken is created from the<br />

CancellationTokenSource. The query is run in a separate thread where the exception of type<br />

OperationCanceledException is caught. This exception is fired if the query is cancelled. From the main<br />

thread the task can be cancelled by invoking the Cancel() method of the CancellationTokenSource.<br />

var cts = new CancellationTokenSource();<br />

new Thread(() =><br />

{<br />

try<br />

{<br />

var sum = (from x in data.AsParallel().<br />

WithCancellation(cts.Token)<br />

where x < 80<br />

select x).Sum();<br />

Console.WriteLine("query finished, sum: {0}", sum);<br />

www.it-ebooks.info

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!