What does “release sequence” mean?

前端 未结 4 2111
你的背包
你的背包 2020-12-30 09:55

I don\'t understand, why will be problems without release sequence, if we have 2 threads in the example below. We have only 2 operations on the atomic variable

4条回答
  •  情书的邮戳
    2020-12-30 10:24

    fetch_sub is read-modify-write action. it atomically reads the value from the memory address, decrement it by the argument provided and then writes it back to the memory address . it all happens atomically.

    now, every atomic action reads and writes directly to the memory address. the CPU does not rely on a value cachced in the registers or the cache-lines for performance gain. it reads and writes to the memory address directly and prevent othr CPU's to do so in that time.

    what "plain" (==relaxed) atomicity does not provide is reordering. both the compiler and the CPU scramble reads and writes in order to speed up the execution of the program.

    look at the example below:

    atomic integer i
    regular integer j
    
    Thread A:
    i <- 5
    //do something else
    i -> j
    //make some decisions regarding j value.
    
    Thread B:
    i++
    

    if no memory order is supllied the compiler and the CPU are allowed to transform the code to

    Thread A:
    i -> j
    i <- 5
    //do something else
    //make some decisions regarding j value.
    
    Thread B:
    i++
    

    Which is of-course not what we wanted. the decision making is wrong.

    what we need is memory reordering.

    memory order acquire: don't scramble memory accesses before
    memory order release: don't scramble memory accesses after

    going back to the question:

    fetch_sub is both reading a value and writing a value. by specifying a memory order acquire we say "I only care about the order of actions the happened before the reading"
    by specifying memory order release we say "I only care about the order of actions happened after the writing.

    But you do care about memory access before and after!

    if you only have one consumer thread, than the sub_fetch does not effect anyone, because the producer anyway uses plain store and the affects of fetch_sub are only visible to the thread which invoked fetch_sub. and in this case, you only care about the reading - the reading gives you the current and updated index. what happens after you store the updated index (lets say x-1) is not that important.

    but since there are two threads which read and write to counter it is important that thread A will be aware that thread B wrote a new value to the counter and Thread B is aware that Thread A is about the read the value of counter. also vice versa- Thread B must be aware that Thread A wrote a new value to counter and Thread A must be aware that Thread B is about to read a value from counter

    you need both guarantees - every thread states that it is about to both read and write to the shared counter. the memory order you need is std::memory_order_acquire_release.

    But the example is tricky. the producer thread simply stores a new value in counter regardless of the value which was there before. if the producer thread was to incremenet the counter each time it pushes new item - you had to use std::memory_order_acquire_release in both the producer and the consumer threads even if you had one consumer

提交回复
热议问题