英文:
Incorrect synchronization in go lang
问题
在我查看golang内存模型文档(链接)时,我发现了一个关于go语言的奇怪行为。这个文档说下面的代码可能会导致g先打印2,然后打印0。
var a, b int
func f() {
a = 1
b = 2
}
func g() {
print(b)
print(a)
}
func main() {
go f()
g()
}
这只是一个go routine的问题吗?因为我很好奇为什么变量'b'的赋值可以在变量'a'之前发生?即使变量'a'和'b'的赋值发生在不同的线程中(不是在主线程中),是否必须确保在它自己的线程中'b'在'a'之前被赋值?(因为'a'的赋值先发生,然后是'b'的赋值)有人可以清楚地告诉我这个问题吗?
英文:
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?
答案1
得分: 12
变量 a
和 b
在任何函数开始执行之前都会被分配和初始化为它们各自类型的零值(在 int
类型的情况下为 0
),代码如下:
var a, b int
可能会改变的是在 f()
函数中为它们分配新值的顺序。
引用自该页面:Happens Before:
> 在单个 goroutine 内部,读取和写入必须表现得好像它们按程序指定的顺序执行。也就是说,编译器和处理器只有在重新排序不会改变该 goroutine 内部行为(由语言规范定义)时,才能重新排序在单个 goroutine 内部执行的读取和写入。由于这种重新排序,一个 goroutine 观察到的执行顺序可能与另一个 goroutine 感知到的顺序不同。例如,如果一个 goroutine 执行 a = 1; b = 2;
,另一个 goroutine 可能在观察到 a
的更新值之前观察到 b
的更新值。
如果重新排序对于同一个 goroutine 内部没有影响,那么对 a
和 b
的赋值可能不会按照你编写的顺序进行。例如,如果首先更改 b
的值更有效(例如,因为它的地址已经加载到寄存器中),编译器可能会重新排序它们。如果更改赋值顺序会导致同一个 goroutine 中出现问题,那么显然编译器不允许改变顺序。由于 f()
函数的 goroutine 在赋值之后不对变量 a
和 b
进行任何操作,编译器可以自由地按任意顺序进行赋值。
由于上面的示例中的两个 goroutine 之间没有同步,编译器不会努力检查重新排序是否会导致其他 goroutine 中出现任何问题。它不需要这样做。
但是,如果你同步你的 goroutine,编译器将确保在“同步点”处不会出现不一致:你将保证在该点上两个赋值都已“完成”;因此,如果“同步点”在 print()
调用之前,你将看到打印出的新赋值:2
和 1
。
英文:
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论