I've heard i++ isn't thread safe, is ++i thread-safe?

后端 未结 16 1717
野性不改
野性不改 2020-11-27 10:38

I\'ve heard that i++ isn\'t a thread-safe statement since in assembly it reduces down to storing the original value as a temp somewhere, incrementing it, and then replacing

相关标签:
16条回答
  • 2020-11-27 11:02

    The 1998 C++ standard has nothing to say about threads, although the next standard (due this year or the next) does. Therefore, you can't say anything intelligent about thread-safety of operations without referring to the implementation. It's not just the processor being used, but the combination of the compiler, the OS, and the thread model.

    In the absence of documentation to the contrary, I wouldn't assume that any action is thread-safe, particularly with multi-core processors (or multi-processor systems). Nor would I trust tests, as thread synchronization problems are likely to come up only by accident.

    Nothing is thread-safe unless you have documentation that says it is for the particular system you're using.

    0 讨论(0)
  • 2020-11-27 11:08

    Throw i into thread local storage; it isn't atomic, but it then doesn't matter.

    0 讨论(0)
  • 2020-11-27 11:10

    You can't make a blanket statement about either ++i or i++. Why? Consider incrementing a 64-bit integer on a 32-bit system. Unless the underlying machine has a quad word "load, increment, store" instruction, incrementing that value is going to require multiple instructions, any of which can be interrupted by a thread context switch.

    In addition, ++i isn't always "add one to the value." In a language like C, incrementing a pointer actually adds the size of the thing pointed to. That is, if i is a pointer to a 32-byte structure, ++i adds 32 bytes. Whereas almost all platforms have an "increment value at memory address" instruction that is atomic, not all have an atomic "add arbitrary value to value at memory address" instruction.

    0 讨论(0)
  • 2020-11-27 11:10

    AFAIK, According to the C++ standard, read/writes to an int are atomic.

    However, all that this does is get rid of the undefined behavior that's associated with a data race.

    But there still will be a data race if both threads try to increment i.

    Imagine the following scenario:

    Let i = 0 initially:

    Thread A reads the value from memory and stores in its own cache. Thread A increments the value by 1.

    Thread B reads the value from memory and stores in its own cache. Thread B increments the value by 1.

    If this is all a single thread you would get i = 2 in memory.

    But with both threads, each thread writes its changes and so Thread A writes i = 1 back to memory, and Thread B writes i = 1 to memory.

    It's well defined, there's no partial destruction or construction or any sort of tearing of an object, but it's still a data race.

    In order to atomically increment i you can use:

    std::atomic<int>::fetch_add(1, std::memory_order_relaxed)

    Relaxed ordering can be used because we don't care where this operation takes place all we care about is that the increment operation is atomic.

    0 讨论(0)
  • 2020-11-27 11:12

    If you want an atomic increment in C++ you can use C++0x libraries (the std::atomic datatype) or something like TBB.

    There was once a time that the GNU coding guidelines said updating datatypes that fit in one word was "usually safe" but that advice is wrong for SMP machines, wrong for some architectures, and wrong when using an optimizing compiler.


    To clarify the "updating one-word datatype" comment:

    It is possible for two CPUs on an SMP machine to write to the same memory location in the same cycle, and then try to propagate the change to the other CPUs and the cache. Even if only one word of data is being written so the writes only take one cycle to complete, they also happen simultaneously so you cannot guarantee which write succeeds. You won't get partially updated data, but one write will disappear because there is no other way to handle this case.

    Compare-and-swap properly coordinates between multiple CPUs, but there is no reason to believe that every variable assignment of one-word datatypes will use compare-and-swap.

    And while an optimizing compiler doesn't affect how a load/store is compiled, it can change when the load/store happens, causing serious trouble if you expect your reads and writes to happen in the same order they appear in the source code (the most famous being double-checked locking does not work in vanilla C++).

    NOTE My original answer also said that Intel 64 bit architecture was broken in dealing with 64 bit data. That is not true, so I edited the answer, but my edit claimed PowerPC chips were broken. That is true when reading immediate values (i.e., constants) into registers (see the two sections named "Loading pointers" under listing 2 and listing 4) . But there is an instruction for loading data from memory in one cycle (lmw), so I've removed that part of my answer.

    0 讨论(0)
  • 2020-11-27 11:15

    According to this assembly lesson on x86, you can atomically add a register to a memory location, so potentially your code may atomically execute '++i' ou 'i++'. But as said in another post, the C ansi does not apply atomicity to '++' opération, so you cannot be sure of what your compiler will generate.

    0 讨论(0)
提交回复
热议问题