Recently, I was building multi-threaded applications and noticed some intermittent data inconsistency. Soon, I found the culprit was locking mechanism. After doing some research, I gathered the following information and as usual decide to share with you guys.
Sharing variables among multiple concurrent threads may lead to data inconsistency. To overcome data inconsistency, we usually apply locking mechanism on shared variables. The lock keyword is used to lock a variable. Once a variable is locked, it will be not be available to other thread until the current thread is done using it. Locking is pretty safe in most of the cases but there are cases when locking may not be the safe solution. Some of these cases are incrementing and decrementing the value of a variable and exchanging two variables with each other. These operations seem atomic operations but actually there are not.
For example, the following code increments the value of counter: lock(this)
Usually, the increment or decrement operations include three steps:
- Load the value of from counter variable to a register.
- Increment the value of counter.
- Store the incremented value back in the variable
The problem with the above code snippet is, a thread can preemptive after first two steps and another thread can start the execution before the incremented value of the variable is saved back in the variable from the register. In the mean time, second thread can go ahead and execute all three steps. After that first thread executes the thread step and overwrites the value of the counter variable. Now the operation executed by second thread is lost.
So how do we can we avoid this scenario? This is where interlocking comes in. The System.Threading.Interlock class provides members that can be used to increment, decrement values and exchange data between two variables. The Interlock class provides atomic operations in variables that are shared among multiple concurrent threads.
The above code snippet can be replaced with the following:
The Interlock class provides following four static methods. All of these methods perform atomic operations:
- CompareExchange: Compares two values for equality and replaces one of them if they are equal.
- Decrement: Decrements the value of a variable.
- Exchange: Sets a variable to a specified value.
- Increment: Increments the value of a variable.
Use Interlock class methods for incrementing, decrementing, and exchange operations.