-2

I'm confuse with the problem, it just come up when I review someone's C++ code.

For example in C++:

// Global var
int g_var = 0;

// thread 1 call Func1() forever:
void Func1() {
  ++g_var;
}

// thread 2 call Func2() forever:
void Func2() {
  --g_var;
}

Define the Func1 call times times1, Func2 call times times2

  • Does times1 - times2 always = g_var?
  • What if the code is in C not in C++?
  • What if the g_var use volatile decoration?
  • Does using atomic_increase/atomic_decrease is the only right way?

What I think, it should use violatile in C++, not ecessary use atomic operation like InterlockedIncrement in Windows, because the assembly code is just one line, one add instruction:

mov         eax,1
add         dword ptr [a],eax  
Gohan
  • 2,422
  • 2
  • 26
  • 45
  • `int g_var = 0;` should be [`std::atomic g_var = 0;`](http://en.cppreference.com/w/cpp/atomic/atomic). – πάντα ῥεῖ Jul 17 '14 at 11:07
  • Possible duplicate of [Why is volatile not considered useful in multithreaded C or C++ programming?](http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming) – πάντα ῥεῖ Jul 17 '14 at 11:11
  • Which assembly instructions are generated for _one_ particular platform doesn't matter. There are processors which would need separate instructions for reading and writing the value. And even one instruction doesn't necessarily mean it is atomic! – BlackJack Jul 17 '14 at 11:16
  • Note that *not* using atomic operations explicitly *might* still be compiled to an assembly instruction which is atomic (i.e. the standard doesn't forbid it!). But you don't have a guarantee that it will do that unless you use atomics explicitly. Even worse: it will be hard to debug since you might think it works, but that's only your luck on this particular machine, but when porting to a different architecture it might break! That's often the case with undefined behavior: it might work in one setup, but isn't guaranteed to do on others. Very frustrating. – leemes Jul 17 '14 at 11:16
  • 1
    `violatile` describes it quite pictorially ;) ... – πάντα ῥεῖ Jul 17 '14 at 11:19
  • thanks all, now I got more understanding. – Gohan Jul 17 '14 at 11:20

2 Answers2

2

Answering your questions in the order of their appearance:

  • No, your program has undefined behavior. It might be the case that thread 1 reads the variable (to be increased), gets interrupted, then thread 2 reads the variable (to be decreased), decreases it and writes it back (so it is minus one compared to the value we started with), then thread 1 continues, increases its memorized old value and writes it back to the variable, resulting in plus one compared to the value we started with.

  • The language doesn't change anything here.

  • volatile only guarantees that a single thread might not cache the value of the variable, i.e. ++g_var; ++g_var; will read the variable, increase, write, read, increase, write. It is not replaced by g_var += 2 by the optimizer. But the thread can still be interrupted in between reading and writing which is the dangerous part.

  • That is a perfect example where you need atomic operations. Either use atomic operations on an int, or use the std::atomic<int> wrapper which automatically does that for you when you call the normal operators, making your code more readable.

    Simply replacing int g_var with std::atomic<int> g_var solves any problems.

leemes
  • 44,967
  • 21
  • 135
  • 183
1
  • No, because the code is not thread safe.
  • C instead of C++ doesn't matter.
  • volatile doesn't help, it prevents ”caching” the value in a register or other optimizations, but doesn't make the operations atomic.
  • Yes. Or no, if implementing such functions yourself with a locking mechanism is an option.
BlackJack
  • 4,476
  • 1
  • 20
  • 25