Lock, Monitor, Interlocked – synchronizing threads

Imagine we have code with two threads. The first one adds one to shared variable. The other one substracts one from shared variable. Both work in a loop.

public void StartWithoutSynchronization()
{
        int x = 0;	
        //this task adds 1 to X in loop
	var taskPlus = new Task(() =>
	{
		for (int i = 0; i < 100000; i++)
		{
			x = x + 1;
		}
	});

	//this task substracts 1 from X in loop
	var taskMinus = new Task(() =>
	{
		for (int i = 0; i < 100000; i++)
		{
			x = x - 1;
		}
	});

	taskPlus.Start();
	taskMinus.Start();
	Task.WaitAll(taskPlus, taskMinus);

	//when both tasks finish we expect x == 0, 
        //but it's not due to the fact that adding and substracting are not atomic opeartions
	Console.WriteLine("Total value: " + x);
}

After executing the code above you will notice that final result is not 0.
We can fix it in a few simple ways.

Locks allows us to synchronize the threads. We make locks on private objects (to lock in current instance) or private static objects (to lock all instances).

        public void StartSynchronizedWithLock()
        {
            int x = 0;
            var xLock = new object();

            //this task adds 1 to X in loop
            var taskPlus = new Task(() =>
            {
                for (int i = 0; i < 100000; i++)
                {
                    // lock causes that only one thread at a time can have access to code in lock body
                    // notice we have lock on the same object in the other task
                    lock (xLock)
                    {
                        x = x + 1;
                    }
                }
            });

            //this task substracts 1 from X in loop
            var taskMinus = new Task(() =>
            {
                for (int i = 0; i < 100000; i++)
                {
                    // lock causes that only one thread at a time can have access to code in lock body
                    // notice we have lock on the same object in the first task
                    lock (xLock)
                    {
                        x = x - 1;
                    }
                }
            });

            taskPlus.Start();
            taskMinus.Start();
            Task.WaitAll(taskPlus, taskMinus);

            //when both tasks finish we expect x == 0 and result really is 0
            Console.WriteLine("Total value: " + x);
        }

Monitor is analogical mechanism to lock, but with more options to use and longer, more complicated syntax. Monitor additionaly have TryEnter method which informs whether thread can access critical section or not.

public void StartSynchronizedWithMonitor()
{
	int x = 0;
	var xLock = new object();

	//this task adds 1 to X in loop
	var taskPlus = new Task(() =>
	{
		for (int i = 0; i < 100000; i++)
		{
			Monitor.Enter(xLock); //monitor blocks thread if another thread is already in critical section
			try
			{
				x = x + 1;
			}
			finally
			{
				Monitor.Exit(xLock); //lock is released in finally clause
			}
		}
	});

	//this task substracts 1 from X in loop
	var taskMinus = new Task(() =>
	{
		for (int i = 0; i < 100000; i++)
		{
			Monitor.Enter(xLock);
			try
			{
				x = x - 1;
			}
			finally
			{
				Monitor.Exit(xLock);
			}
		}
	});

	taskPlus.Start();
	taskMinus.Start();
	Task.WaitAll(taskPlus, taskMinus);

	//when both tasks finish we expect x == 0 and result really is 0
	Console.WriteLine("Total value: " + x);
}

Interlocked it’s a class which exposes a few atomic methods (i.e. .Increment, .Add, .Exchange).

public void StartSynchronizedWithInterlocked()
{
	//this task adds 1 to X in loop
	var taskPlus = new Task(() =>
	{
		for (int i = 0; i < 100000; i++)
		{
			Interlocked.Add(ref x, 1); // this add operation is atomic
		}
	});

	//this task substracts 1 from X in loop
	var taskMinus = new Task(() =>
	{
		for (int i = 0; i < 100000; i++)
		{
			Interlocked.Add(ref x, -1); // this add operation is atomic
		}
	});

	taskPlus.Start();
	taskMinus.Start();
	Task.WaitAll(taskPlus, taskMinus);

	//when both tasks finish we expect x == 0 and result really is 0
	Console.WriteLine("Total value: " + x);
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s