Anytime a thread tries to access some memory or resource that another thread can change, you have a race condition. There is no winner for this kind of race. The whole application will lose.
This episode describes first explains what a race condition is and then explains two ways to avoid race conditions. You can use atomic operations or critical sections.
The problem that makes this type of bug difficult to catch is that you may not notice it. You could test your software and find no problems. After passing all the tests, you release it to the world. And that’s where the problems begin. You’ve just exposed your software to environments that are different than what you tested.
You can use atomic operations if all you need to do is modify a single numeric value. Anything more complicated even if it’s a series of atomic operations will need a critical section to protect the entire sequence.