Suggested Videos
Part 90 - Passing data to the Thread function in a type safe manner
Part 91 - Retrieving data from Thread function using callback method
Part 92 - Significance of Thread.Join and Thread.IsAlive functions
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. Let us understand this with an example.
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.
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
The other option is to use a lock.
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.
Please Note: You can use the TimeSpan object to find ticks per second, ticks per millisecond etc. Stopwatch class is in System.Diagnostics namespace.
Part 90 - Passing data to the Thread function in a type safe manner
Part 91 - Retrieving data from Thread function using callback method
Part 92 - Significance of Thread.Join and Thread.IsAlive functions
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. 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. Stopwatch class is in System.Diagnostics namespace.
Hello Venkat, I am trying to run the same code without using Lock or Interlock but getting same output each time. Infact I ran it around 15-20 times but each time got the correct output. Could you suggest how is that working.
ReplyDelete