A. From the theoretical point of view given this program:
Initial:
x = 0
Thread 1:
x = 1
Thread 2:
x1 = x
x2 = x
If x1 = 1, we have the following happen-before relationship:
- x = 0 happens before x = 1
- x = 0 happens before x1 = x
- x1 = x happens before x2 = x
Note that there is no x = 1 happening before x1 = x since these are not synchronising operations.
As a result, there is no happen-before relationship between x = 1 and x2 = x, which means these 2 are racy, and the read can observe either value of 0 or 1.
B. From the practical point of view, given the 2 reads:
x1 = x
x2 = x
The compiler, CPU, etc can freely reorder these 2 operations, which leads to the second load executing before the first one. Worse, even if the first load seems to come before the second one from the perspective of a thread, there is no guarantee that another thread may observe the opposite behaviour, that is the second load may result in some value that apparently impossible at that point. This may lead to paradoxes when trying to reason the behaviour of a program using some sequentially consistent order of a permutation of a program order.
The ability to prevent the reordering of 2 loads with respect to every thread is called a load-load fence. Suppose Go will support memory barriers in the future, your program can be rewritten as:
```go
package main
import "memory"
var x int = 0
func main() {
go func() {
x = 1
}()
print(x) // first read: 1
memory.LoadLoadFence()
print(x) // second read: 0
}
```
and we can hope that the program will never print out 1, 0.
Thanks.