Incorrect synchronization in go lang
Asked Answered
L

1

6

While I was taking a look on the golang memory model document(link), I found a weird behavior on go lang. This document says that below code can happen that g prints 2 and then 0.

var a, b int

func f() {
    a = 1
    b = 2
}

func g() {
    print(b)
    print(a)
}

func main() {
    go f()
    g()
}

Is this only go routine issue? Because I am curious that why value assignment of variable 'b' can happen before that of 'a'? Even if value assignment of 'a' and 'b would happen in different thread(not in main thread), does it have to be ensured that 'a' should be assigned before 'b' in it's own thread?(because assignment of 'a' comes first and that of 'b' comes later) Can anyone please tell me about this issue clearly?

Lesseps answered 22/10, 2015 at 6:48 Comment(1)
As I understand it: visibility of the value of b in g is not determined by f. Inside f a is set first and b afterwards and never the other way around.Hummingbird
D
12

Variables a and b are allocated and initialized with the zero values of their respective type (which is 0 in case of int) before any of the functions start to execute, at this line:

var a, b int

What may change is the order new values are assigned to them in the f() function.

Quoting from that page: Happens Before:

Within a single goroutine, reads and writes must behave as if they executed in the order specified by the program. That is, compilers and processors may reorder the reads and writes executed within a single goroutine only when the reordering does not change the behavior within that goroutine as defined by the language specification. Because of this reordering, the execution order observed by one goroutine may differ from the order perceived by another. For example, if one goroutine executes a = 1; b = 2;, another might observe the updated value of b before the updated value of a.

Assignment to a and b may not happen in the order you write them if reordering them does not make a difference in the same goroutine. The compiler may reorder them for example if first changing the value of b is more efficient (e.g. because its address is already loaded in a register). If changing the assignment order would (or may) cause issue in the same goroutine, then obviously the compiler is not allowed to change the order. Since the goroutine of the f() function does nothing with the variables a and b after the assignment, the compiler is free to carry out the assignments in whatever order.

Since there is no synchronization between the 2 goroutines in the above example, the compiler makes no effort to check whether reordering would cause any issues in the other goroutine. It doesn't have to.

Buf if you synchronize your goroutines, the compiler will make sure that at the "synchronization point" there will be no inconsistencies: you will have guarantee that at that point both the assignments will be "completed"; so if the "synchronization point" is before the print() calls, then you will see the assigned new values printed: 2 and 1.

Distinguished answered 22/10, 2015 at 7:1 Comment(3)
Thanks icza. Hmm.. this is a compiler and processor issue then. (I mean this is not a problem but the cause is compiler and processor.)Lesseps
@KyuntaeEthanKim It's an optimization and a synchronization issue: your goroutines are not synchronized so you have no guarantee what and if any of the updated values are observed by the other goroutine. It may print 2 and 1, it may print 2 and 0, it may print 0 and 1 and it may print 0 and 0.Distinguished
So writing f() like: a=1;print(a+b);b=2;print(a+b) would guarantee that g() can't print 0 2?Wellesz

© 2022 - 2024 — McMap. All rights reserved.