Wednesday 29 October 2014

Forgetting the PLINQ Basics


Creating Race Conditions 


A common trick with LINQ is to use the where clause to filter items from the source data while some external value has a specified value and is modified inside the where clause itself. This presents a data race with PLINQ because multiple Tasks can be reading and modifying the value at once.


Example 

The following example uses a shared value to limit the number of items that the where clause filters for processing and so suffers from a race condition:

using System;
using System.Linq;

namespace Creating_Race_Conditions {
    class Creating_Race_Conditions {
        static void Main(string[] args) {

            // create some source data
            int[] sourceData = new int[10000];
            for (int i = 0; i < sourceData.Length; i++) {
                sourceData[i] = i;
            }

            // create a counter - this will be shared
            int counter = 1000;

            // create a plinq query that uses the shared value
            var result = from e in sourceData.AsParallel()
                         where (counter-- > 0)
                         select e;

            Console.WriteLine("Expected {0} items", counter);
            Console.WriteLine("Got {0} items", result.ToArray().Length);
        }
    }

Solution:

Perform the filter sequentially before processing the data in parallel. Or find a PLINQ method that will work safely with multiple threads (e.g., the Take() method would be a suitable alternative to the example code for this problem). As a last resort, use a synchronization primitive to control access to the shared value.

In my next blog i will describe some advanced synchronization primitives that will solve the race contition