I know it can happen that g prints 2 and then 0 given the following code.
var a, b uint32
func f() {
a = 1
b = 2
}
func g() {
fmt.Println(b)
fmt.Println(a)
}
func main() {
go f()
g()
}
What if I change all read and write to atomic operations? Is that guaranteed that if g prints 2 first, then 1 is also printed?
var a, b uint32
func f() {
atomic.StoreUint32(&a, 1)
atomic.StoreUint32(&b, 2)
}
func g() {
fmt.Println(atomic.LoadUint32(&b))
fmt.Println(atomic.LoadUint32(&a))
}
func main() {
go f()
g()
}
No. There's no synchronization, so there is no "happens before" relationship.
Synchronization between goroutines is done via channel communication and lock operations.
A key paragraph in the memory model is:
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.
Practically it will work as you described. The Go compiler won't re-order atomic operations and atomic store is implemented using XCHG on amd64 (and similar instructions on other architectures): https://github.com/golang/go/blob/release-branch.go1.8/src/cmd/compile/internal/ssa/gen/AMD64.rules#L472
This behaviour is not specified at the moment (as of Go 1.8) and might change, see discussion at https://github.com/golang/go/issues/5045 for more insights.