Introduction
This article describes the thread synchronization technique with interlock
class present in .NET Framework. Interlock
class provides atomic operation on shared variable. The operating system will not interrupt their operation at the middle of its execution.
Interlock
class provides a different thread safe method for different operations with limited data type support. This class is used when we need to increment, decrement, exchange, etc. on shared variable between multiple threads.
Issue without Synchronization
Consider the following line of code which increments the shared variable X inside the multi threading environment.
int X=0;
X=X+1;
From the programming point of view, the line X=X+1
is an atomic operation. But internally to execute this line, the computer needs to do more. We can break the above line in three operations:
- Move the value of X to CPU register.
- Increment the value in register.
- Save the value back to memory.
Suppose there are 2 threads executing the same line of code. Imagine the first thread is in step 2 means the value if X
has incremented to 1
. At this point of time, the operating system stops the first thread. The second is also running in parallel. The second thread finishes all three steps and updates the value of X
to 1
. Now assume the first thread is resuming its operation and executes the third step. At this step, the value of X
is updated from 0
to 1
which is the old value get updated. This means that after executing the above line of code, the value of X
would be 1
which is incorrect. The value of X
should be 2
.
Resolution
By using interlocked
class, we can solve this issue and can get the expected value which is 2
. Interlocked
class provides a simple thread safe method Interlocked
. Increment (ref variable)
for increment operation. It ensures that the operating system will not interrupt the Increment Operation.
Using the Code
It’s very common for every developer to Increment, Decrement and Exchange operation on shared variable in multithreaded environment. Without synchronization, we may not get the result as expected. But with thread synchronization, we can get the expected result. In the following section, I have described the actual implementation in both scenarios.
Shared variable Increment without Synchronization
The following code increments the shared variable by multiple threads without any synchronization.
int Number = 0;
private void btnWithSync_Click(object sender, EventArgs e)
{
int TotalThread = 10000;
Thread[] IntreLockThread = new Thread[TotalThread];
for (int i = 0; i < TotalThread; i++)
{
IntreLockThread[i] = new Thread(new ThreadStart(UpdateValue));
IntreLockThread[i].IsBackground = true;
IntreLockThread[i].Priority = ThreadPriority.Lowest;
IntreLockThread[i].Start();
}
for (int i = 0; i < TotalThread; i++)
{
IntreLockThread[i].Join();
}
lblTotalValue.Text = Number.ToString();
}
public void UpdateValue()
{
listBox1.BeginInvoke(new ParameterizedThreadStart(UpdateListBox1),
"Thread ID : " +
Thread.CurrentThread.ManagedThreadId + " - B:: =" + Number.ToString());
Number++;
listBox1.BeginInvoke(new ParameterizedThreadStart(UpdateListBox1),
"Thread ID : " +
Thread.CurrentThread.ManagedThreadId + " - A:: =" + Number.ToString());
}
public void UpdateListBox1(object objResult)
{
listBox1.Items.Add(objResult.ToString());
}
In the above code, I have run 10000 threads in a row to increment the shared variable. But at the end, all thread execution is complete; the value of the variable should be 10000. But partially it’s not happening. Sometimes, the value is less than 10000 (in my last run, the value was 9995). This happens due to context switching between threads. Operating system can interrupt the current executing thread and stop execution at the middle of increment and later on, it resumes the operation. This will replace the value of variable with old value. So the increment operation in this way is not atomic. We can use interlocked
class to resolve this issue.
Shared Variable Increment with Synchronization
The following code increments the shared variable by multiple threads with the help of interlocked
class. The Increment
method of interlocked
class is used for this purpose.
int Number = 0;
private void brnWithSync_Click(object sender, EventArgs e)
{
Number = 0;
int TotalThread = 10000;
Thread[] IntreLockThread = new Thread[TotalThread];
for (int i = 0; i < TotalThread; i++)
{
IntreLockThread[i] = new Thread(new ThreadStart(UpdateWithInterlock));
IntreLockThread[i].IsBackground = true;
IntreLockThread[i].Start();
}
for (int i = 0; i < TotalThread; i++)
{
IntreLockThread[i].Join();
}
lblTotalValueSync.Text = Number.ToString();
}
public void UpdateWithInterlock()
{
listBox2.BeginInvoke(new ParameterizedThreadStart(UpdateListBox2),
"Thread ID : " +
Thread.CurrentThread.ManagedThreadId + " - B:: =" + Number.ToString());
Interlocked.Increment(ref Number);
listBox2.BeginInvoke(new ParameterizedThreadStart(UpdateListBox2),
"Thread ID : " +
Thread.CurrentThread.ManagedThreadId + " - A:: =" + Number.ToString());
}
public void UpdateListBox2(object objResult)
{
listBox2.Items.Add(objResult.ToString());
}
As previous, I have run 10000 threads in a row to increment the shared variable. At the end of execution, I got the expected result. The value of the variable is incremented to 10000
. So the increment operation in this way is atomic and it’s fully synchronized.
Interlocked
class supports more operations along with Increment
. All methods of this class take the first parameter as reference type so that we can get the updated value after the operation completes. All non generic methods of this class have overloaded versions that exist. Here I have described only one version.
Following are the different thread safe methods of interlocked
class:
Interlocked.Add(ref int intNumber,int value);
This method adds the value of two parameters and replaces the first one with the sum of both values as an atomic operation.
Interlocked.Increment(ref int intNumber);
This method increments the value by 1
and assigns the updated value to itself as an atomic operation.
Interlocked.Decrement(ref int intNumber);
This method decrement the value by 1
and assigns the updated value to itself as an atomic operation.
Interlocked.Read(ref int intNumber);
It returns the value of variable specified in parameter.
Interlocked.Exchange(ref intNumber1, int intNumber2);
The exchange method assigns the value of first parameter with the value of second parameter as an atomic operation. It’s basically a set operation. Someone can think why we should use this function as the new value doesn’t depend on the old one. But it's necessary multi-processor or multi-core machine environment.
Interlocked.Exchange<T>(ref T Location, T value)
This method is a generic version of exchange
method. The functionality is the same as Exchange
method. Here it must be reference type.
Interlocked.CompareExchange(ref int intNumber1,int intNumber2,int CompareValue);
CompareExchange
is a conditional assignment method. The first parameter is compared with the last parameter. If both are equal, then the value of the first parameter is replaced by the second parameter.
Interlocked.CompareExchange<T>(ref T Location, T Value, T CompareValue);
This method is a generic version of CompareExchange
method. The functionality is the same as Exchange
method. Here T
must be reference type.
Testing the Code
To test the above code, run the application. When running the code without synchronization by clicking on “Run without Synchronization” button, please try to keep the CPU busy by opening a new different application so that context switch happens. To run the code with synchronization using interlocked
class, click on “Run with Synchronization” and get the actual result as the expected result.
Conclusion
I have quite enjoyed writing this article. I think this article gives the basic overview of interlock
class, a thread synchronization technique provided in .NET Framework.
History
- 20th November, 2011: Initial version
- 22nd November, 2011: Article updated