问题
On a multicore x86 machine, Say a thread executing on core1 increments an integer variable a at the same time thread on core 2 also increments it. Given that the initial value of a was 0, would it always be 2 in the end? Or it could have some other value? Assume that a is declared as volatile and we are not using atomic variables (such as atomic<> of C++ and built in atomic operations in gcc).
If the value of a would indeed be always 2 in such event, does that mean that a long int in x86-64 would also have the same property, that is, a will always be 2 in the end?
回答1:
The increment-memory machine instruction on an X86 is atomic only if you use it with a LOCK prefix.
x++ in C and C++ doesn't have atomic behavior. If you do unlocked increments, due to races in which processor is reading and writing X, if two separate processors attempt an increment, you can end up with just one increment or both being seen (the second processor may have read the initial value, incremented it, and written it back after the first writes its results back).
I believe that C++11 offers atomic increments, and most vendor compilers have an idiomatic way to cause an atomic increment of certain built-in integer types (typically int and long); see your compiler reference manual.
If you want to increment a "large value" (say, a multiprecision integer), you need to do so with using some standard locking mechanism such as a semaphore.
Note that you need to worry about atomic reads, too. On the x86, reading a 32 or 64 bit value happens to be atomic if it is 64-bit word aligned. That won't be true of a "large value"; again you'll need some standard lock.
回答2:
Here's one proof it is not atomic in a particular implementation (gcc), As you can see (?), gcc generates code that
- loads the value from memory to a register
- increments the content of the register
- saves the register back to memory.
That's very far from being atomic.
$ cat t.c
volatile int a;
void func(void)
{
a++;
}
[19:51:52 0 ~] $ gcc -O2 -c t.c
[19:51:55 0 ~] $ objdump -d t.o
t.o: file format elf32-i386
Disassembly of section .text:
00000000 <func>:
0: a1 00 00 00 00 mov 0x0,%eax
5: 83 c0 01 add $0x1,%eax
8: a3 00 00 00 00 mov %eax,0x0
d: c3 ret
Don't be fooled by the 0x0 in the mov instruction, there's room for 4 bytes there, and the linker will fill in the resulting memory address for a there when this object file is linked.
回答3:
Since no one has answered your actual question and instead are showing you how to do it in a way that always works:
Thread 1 loads value of 0
Thread 2 loads value of 0
Thread 1 increments an stores 1
Thread 2 increments its local register copy of value and stores 1.
As you can see the end result is a value equal to 1 and not 2. It will not always be 2 at the end.
回答4:
It’s not guaranteed. You can use the lock xadd instruction to achieve the same effect, or use C++ std::atomic, or use #pragma omp atomic, or any number of other concurrency solutions that have been written to save you the trouble of reinventing the wheel.
来源:https://stackoverflow.com/questions/10503737/is-increment-an-integer-atomic-in-x86