Why am I ++ not thread safe on one main machine?

Possible duplicate:
Are increments / decrements (var ++, var--), etc. safe?

Can you describe for me, at the assembly code level, why incrementing values ​​from two different threads is not considered safe on the same main machine?

+4
source share
7 answers

Consider the instructions that can be generated for an operator of type i++ . Of course, this will depend on your architecture / instruction set, but it will probably look like this:

 LOAD @i, r0 ;load the value of 'i' into a register from memory ADD r0, 1 ;increment the value in the register STORE r0, @i ;write the updated value back to memory 

Now consider how multi-threading will be implemented in the operating system, regardless of how many cores it has. At the most basic level of the OS, some means will be required to interrupt the execution of the current thread, save its state and perform a contextual transition to another thread. The OS does not have an automatic way to find out which instructions within a user thread should be considered an atomic operation, and has the ability to initiate a context switch between any two instructions.

So, what happens if the OS switches context from one thread to another between LOAD and ADD ? Let's say that i started with the value 0, so r0 will be set to 0 when the first thread is replaced. The OS will save this value as part of this thread state. Now the second thread executes and executes the same LOAD statement. The value in memory is still 0, so r0 is loaded again. 0. The thread increments the value and writes it back to the memory, setting the value of i to 1. Now the first thread resumes execution, and the operating system restores the value of r0 to 0 as part of its context switch. The first thread now performs its increment, setting r0 to 1, and the value 1 is again stored in i . Now the value of i is incorrect, because two increments were applied, but the value only increased by 1.

So in a nutshell, although i++ is a single statement in a high-level language, it generates several assembler instructions, and these instructions will not be treated as atomic in the operating system / runtime environment, unless you add additional synchronization logic around them.

+9
source

i++ has three operations:

  • Extract i to register
  • Register increase
  • Write it back to i

Between these operations, the thread may be interrupted by the scheduler, so that another thread may execute (and change i ).

+9
source

Your question is marked by assembler, but asks about i ++. You have no guarantee that i++ in your C code will be compiled with one instruction that changes memory. If you have several threads that load i from memory with one instruction, increment it with another and write it back to memory with the third, the thread switch between the first and third of them may cause some updates i lost.

+2
source

In the stream, one will read the old value

Timer interrupt disabled

The kernel resumes thread two

thread 2 reads the old value

the flow doubles it

stream two writes it

Timer

switched off

The kernel resumes thread 1

flow increases in increments

one stream stores

now you are behind.

+1
source

If the processor does not have one instruction that can increase the contents of the memory cell, the compiler will have to do something like generating:

  load location, registerA increment registerA store registerA, location 

Therefore, even if any one instruction is atomic, the sequence is not. And even if there is one

 increment location 

there is no guarantee that the compiler will use it. For example, the compiler may have done some optimization and uses the register to store some commonly used value, storing it back to memory, from time to time determined by any sequence rules in the memory model of the compiler language.

+1
source

What makes the system delete one thread between the time when it reads the value and the time when it wrote the value? Of course, this will be less likely, but in standard operating systems, the kernel may receive an interrupt at any time and decide that another thread deserves to be launched. At this point, both threads will read the same value, and both will increase equally. However, the second thread can work for another time fragment, increasing thousands of times, and then, when the first thread is transferred, it will block the entire progression of the second thread, recording the outdated value.

0
source

The sequence of instructions executed from 2 threads on the same core cannot be predicted. The following is a possible sequence when both threads try to execute i ++, but the effect is equivalent to executing i ++ once:

 load i # thread 1 system interrupt load i # thread 2, now i++ in thread 1 is not complete increment i # thread 2 store i # thread 2 system interrupt increment i # thread 1, actually the same value store i 
0
source

All Articles