为什么最后一个 goroutine 在有两个 goroutine 的循环中首先开始?
Why the last goroutine starts first in a loop with two goroutines?
我一直在用 goroutines 做一些测试,我注意到使用这段代码有一个奇怪的行为(对我来说):
游乐场:https://play.golang.org/p/Py4oqGqkYKm
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
wg sync.WaitGroup
)
func main() {
var x int
for i := 0; i < 10; i++ {
wg.Add(2)
go func() {
mu.Lock()
x = i
mu.Unlock()
wg.Done()
}()
go func() {
mu.Lock()
fmt.Print("x: ", x, " \n")
mu.Unlock()
wg.Done()
}()
wg.Wait()
}
}
我期望输出如下:
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
但是我收到了:
x: 0
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
看起来第二个 goroutine 被首先调用(就像 LIFO)。考虑到这一点,我尝试反转 goroutines 并且收到了我预期的答案:
游乐场:https://play.golang.org/p/BC1r3NK6RBm
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
wg sync.WaitGroup
)
func main() {
var x int
for i := 0; i < 10; i++ {
wg.Add(2)
go func() {
mu.Lock()
fmt.Print("x: ", x, " \n")
mu.Unlock()
wg.Done()
}()
go func() {
mu.Lock()
x = i
mu.Unlock()
wg.Done()
}()
wg.Wait()
}
}
输出:
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
谁能帮我理解这种行为?
转到版本:
go version go1.16.2 linux/amd64
Go 例程提供并发性,并在具有独立堆栈内存的共享堆分配之上工作。它们的目的是在多核系统之上提供并发执行。它们如何并行执行没有任何控制因素。这是由实际处理器决定的。我们仅通过抽象层使用处理器。
实际上没有可以govern/predict结果的并发系统。
Go 语言没有指定 goroutines 在显式同步之外使用通道、互斥锁、等待组等执行的顺序。该规范允许两个程序的两个输出。
您正在观察获取互斥量的顺序,而不是 goroutine 启动的顺序。可能是 goroutines 以您预期的顺序启动,但意外的 goroutine 先调用了 Lock()。
程序确保的唯一顺序是通过等待组:每一对 goroutine 将在下一对 goroutine 开始之前完成。
我一直在用 goroutines 做一些测试,我注意到使用这段代码有一个奇怪的行为(对我来说):
游乐场:https://play.golang.org/p/Py4oqGqkYKm
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
wg sync.WaitGroup
)
func main() {
var x int
for i := 0; i < 10; i++ {
wg.Add(2)
go func() {
mu.Lock()
x = i
mu.Unlock()
wg.Done()
}()
go func() {
mu.Lock()
fmt.Print("x: ", x, " \n")
mu.Unlock()
wg.Done()
}()
wg.Wait()
}
}
我期望输出如下:
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
但是我收到了:
x: 0
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
看起来第二个 goroutine 被首先调用(就像 LIFO)。考虑到这一点,我尝试反转 goroutines 并且收到了我预期的答案:
游乐场:https://play.golang.org/p/BC1r3NK6RBm
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
wg sync.WaitGroup
)
func main() {
var x int
for i := 0; i < 10; i++ {
wg.Add(2)
go func() {
mu.Lock()
fmt.Print("x: ", x, " \n")
mu.Unlock()
wg.Done()
}()
go func() {
mu.Lock()
x = i
mu.Unlock()
wg.Done()
}()
wg.Wait()
}
}
输出:
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
谁能帮我理解这种行为?
转到版本:
go version go1.16.2 linux/amd64
Go 例程提供并发性,并在具有独立堆栈内存的共享堆分配之上工作。它们的目的是在多核系统之上提供并发执行。它们如何并行执行没有任何控制因素。这是由实际处理器决定的。我们仅通过抽象层使用处理器。
实际上没有可以govern/predict结果的并发系统。
Go 语言没有指定 goroutines 在显式同步之外使用通道、互斥锁、等待组等执行的顺序。该规范允许两个程序的两个输出。
您正在观察获取互斥量的顺序,而不是 goroutine 启动的顺序。可能是 goroutines 以您预期的顺序启动,但意外的 goroutine 先调用了 Lock()。
程序确保的唯一顺序是通过等待组:每一对 goroutine 将在下一对 goroutine 开始之前完成。