Jason King Team : Web Development Tags : Web Development Tips & Tricks Performance

Parallel code considerations

Jason King Team : Web Development Tags : Web Development Tips & Tricks Performance

Parallel code is awesome and it gives a user the impression that the code is running much faster. A slow batch process can easily be made 10 times faster by moving the code into a parallel loop.

Writing parallel code in .NET is very easy if you have .NET 4.0 or above however there are a few things to be careful of. When you use the System.Threading.Tasks.Parallel class to create a For or ForEach loop, you are effectively telling the .NET framework to create multiple threads and process the code block simultaneously. You need to be careful that any shared objects you access inside the code block are thread safe. Here’s an example of some bad code:

[TestMethod]
public void TestBadIncrementInThread()
{
    int count = 0;
    int iterations = 1000;
    Parallel.For(0, iterations, i =>
        {
            // run some code that takes a while e.g. call a web service
            var recordsAffected = 1; // webservice.callService(....);
            // update the total records affected
            count += recordsAffected;
        });
    Assert.AreEqual(iterations, count, "Increment got lost.");
}

If you run this test a few times you’ll notice that it fails. This is because the “count += recordsAffected” code is being run on multiple threads and one thread is overwriting the other.

The correct way to add a value to a shared variable (count in our case) is with the System.Threading.Interlocked class e.g. Interlocked.Add(ref count, recordsAffected); There are various other useful methods in this class and you can find more about it at https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx

Another mistake to be aware of is sharing database connections between threads. For example this code will randomly fail because the connection might be open while another thread is trying to use it.

[TestMethod]
public void TestBadSharedContextInThread()
{
    var random = new Random();
    var context = new DbContext();
    int iterations = 2;

    Parallel.For(0, iterations, i =>
    {
        // run some code that takes a while e.g. call a web service
        // e.g. webservice.performUpdate(....);

        // simulate varying web service execution times between 1 and 5 seconds
        Thread.Sleep(random.Next(1000, 5000));

        // get something from the db
        var firstLog = context.EventLogs.FirstOrDefault();
    });
}

To fix this, the context needs to be created inside the Parallel.For code block.