How to overcome
Use Interlocked.Increment
and Interlocked.Decrement
to safely change the value of the counter.
Why this happens
You have counter as variable, which is shared across multiple threads. This variable is non-volatile and not wrapped by any synchronization block, so each thread has its own copy of that variable. So if two threads try to change it value at the same time, value would be overrriden by copy from thread which accessed it last.
Imagine you start your code in two different threads:
- Initially counter equals zero, both threads havy copy of that
- Both thread invoke increment their cached copies, and than change counter. So thread1 increments its copy to 1 and overrides counter, thread2 also increments its copy (still equal to zero) to 1 and overrides counter to, again, 1. After that that value is propagated to all threads (all copies are refreshed)
- Both threads invoke sql query. Due to variability in sql performance, these queries are completed in different time.
- Thread1 ends sql query, decrements counter from 1 to 0. Counter value is propagated to all threads
- After some time, Thread2 ends sql query, decrement counter from already propagated 0 to -1. Counter value is propagated to all threads. And it is -1.