Part 93 Protecting shared resources from concurrent access in multithreading04:33

  • 0
Published on June 3, 2017

Text version of the video

Slides

All C# Text Articles

All C# Slides

All Dot Net and SQL Server Tutorials

In this video we will discuss,
1. What happens if shared resources are not protected from concurrent access in multithreaded program
2. How to protect shared resources from concurrent access

What happens if shared resources are not protected from concurrent access in multithreaded program
The output or behaviour of the program can become inconsistent if the shared resources are not protected from concurrent access in multithreaded program. Let us understand this with an example.

using System;
class Program
{
static int Total = 0;
public static void Main()
{
AddOneMillion();
AddOneMillion();
AddOneMillion();
Console.WriteLine(“Total = ” + Total);
}

public static void AddOneMillion()
{
for (int i = 1; i [= 1000000; i++)
{
Total++;
}
}
}

The above program is a single-threaded program. In the Main() method, AddOneMillion() method is called 3 times, and it updates the Total field correctly as expected, and finally prints the correct total i.e 3000000.

Now, let’s rewrite the program using multiple threads.
using System;
using System.Threading;
class Program
{
static int Total = 0;
public static void Main()
{
Thread thread1 = new Thread(Program.AddOneMillion);
Thread thread2 = new Thread(Program.AddOneMillion);
Thread thread3 = new Thread(Program.AddOneMillion);

thread1.Start();
thread2.Start();
thread3.Start();

thread1.Join();
thread2.Join();
thread3.Join();

Console.WriteLine(“Total = ” + Total);
}

public static void AddOneMillion()
{
for (int i = 1; i [= 1000000; i++)
{
Total++;
}
}
}

Every time we run the above program, we get a different output. The inconsistent output is because the Total field which is a shared resource is not protected from concurrent access by multiple threads. The operator ++ is not thread safe. There are several ways to fix this. Let’s explore 2 of the options.

Using Interlocked.Increment() method: Modify AddOneMillion() method as shown below. The Interlocked.Increment() Method, increments a specified variable and stores the result, as an atomic operation
public static void AddOneMillion()
{
for (int i = 1; i [= 1000000; i++)
{
Interlocked.Increment(ref Total);
}
}

The other option is to use a lock.
static object _lock = new object();

public static void AddOneMillion()
{
for (int i = 1; i [= 1000000; i++)
{
lock (_lock)
{
Total++;
}
}
}

Which option is better?
From a performance perspective using Interlocked class is better over using locking. Locking locks out all the other threads except a single thread to read and increment the Total variable. This will ensure that the Total variable is updated safely. The downside is that since all the other threads are locked out, there is a performance hit.

The Interlocked class can be used with addition/subtraction (increment, decrement, add, etc.) on and int or long field. The Interlocked class has methods for incrementing, decrementing, adding, and reading variables atomically.

The following code prints the time taken in ticks. 1 millisecond consists of 10000 ticks.
public static void Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();
Thread thread1 = new Thread(Program.AddOneMillion);
Thread thread2 = new Thread(Program.AddOneMillion);
Thread thread3 = new Thread(Program.AddOneMillion);

thread1.Start();
thread2.Start();
thread3.Start();

thread1.Join();
thread2.Join();
thread3.Join();

Console.WriteLine(“Total = ” + Total);

stopwatch.Stop();
Console.WriteLine(“Time Taken in Ticks = ” + stopwatch.ElapsedTicks);
}

Please Note: You can use the TimeSpan object to find ticks per second, ticks per millisecond etc.

Enjoyed this video?
"No Thanks. Please Close This Box!"