by

Thread-Safety Using the Lock Keyword

In one of my first blog posts I covered Parallel.Foreach loops. In this post I want to cover a way to protect yourself from the biggest pitfall of concurrent processing using the lock keyword to make your programs thread-safe.

So what does it mean when a program is thread-safe? Basically, when a program has many concurrent threads all running at once, sometimes it ends up fighting itself to access certain resources all at once. One good example of this is when all the threads attempt to use the same global variables. This results in some strange behavior that is unpredictable to say the least.

In my former blog post I showed you how you can print out a bunch of strings at once using Parallel.Foreach. I will now show two examples that do virtually the same thing, but this time one will not be thread-safe and will exhibit some strange behavior. The other will be thread-safe and will behave as predicted.

The Problem:

This time we want to print out a number that will be associated with each city. Each time the loop executes we want to increase the number so that we have something like this:

  1. Houston
  2. New York
  3. Seattle
  4. Helena
  5. Atlanta
  6. Chicago

We won’t be able to guarantee the order of the cities that are printed, but we should be able to ensure that the numbers 1-6 are included somewhere. No repeating values should be present and none of the numbers should be skipped.

So here’s some code that clearly won’t work:

class Program
{
    public static int counter = 0;

    static void Main(string[] args)
    {
        List<string> strings = new List<string>();
        strings.Add("New York");
        strings.Add("Chicago");
        strings.Add("Houston");
        strings.Add("Seattle");
        strings.Add("Helena");
        strings.Add("Atlanta");

        Parallel.ForEach(strings, (currentString) =>
        {
            counter = counter + 1;
            Console.WriteLine(counter + ". " + currentString);
        });

        Console.ReadLine();
    }
}

So, this works sometimes but other times you get weird stuff coming through the output. So how do we prevent that? The answer is with the lock keyword. The following code will avoid any deadlock by preventing any two threads from entering the locked block at the same time. Since the body of our loop is wrapped in the lock block it defeats the purpose of the Parallel.Foreach, but I think it’s still a good example of how locks work.

The Solution:

class Program
{

    private static object lockObject = new object();
    public static int counter = 0;

    static void Main(string[] args)
    {
        List<string> strings = new List<string>();
        strings.Add("New York");
        strings.Add("Chicago");
        strings.Add("Houston");
        strings.Add("Seattle");
        strings.Add("Helena");
        strings.Add("Atlanta");

        Parallel.ForEach(strings, (currentString) =>
        {

            lock(lockObject)

            {
                counter = counter + 1;

                Console.WriteLine(counter+ ". " + currentString);

            }
        });

        Console.ReadLine();
    }
}

Write a Comment

Comment