英文:
Why the last goroutine starts first in a loop with two goroutines?
问题
我已经对goroutines进行了一些测试,并注意到了在使用这段代码时出现了一种奇怪的行为(对我来说)。以下是代码:
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)。考虑到这一点,我尝试颠倒goroutine的顺序,我得到了我期望的答案:
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版本:
go version go1.16.2 linux/amd64
英文:
I have been doing some tests with goroutines and I noticed a weird behaviour (for me) using this code:
Playground: 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()
}
}
I have expected as output something such as:
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
But I have received:
x: 0
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
It looks like that the second goroutines is called first (like LIFO). Thinking that, I have tried to invert the goroutines and I received the answer that I have expected:
Playground: 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()
}
}
Output:
x: 0
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
Could anyone help me to understand this behavior?
Go version:
go version go1.16.2 linux/amd64
答案1
得分: 4
Go语言不指定goroutine在没有使用通道、互斥锁、等待组等显式同步的情况下执行的顺序。规范允许两个程序的输出都有可能。
你观察到的是互斥锁的获取顺序,而不是goroutine启动的顺序。可能是goroutine按照你期望的顺序启动,但是意外的goroutine先调用了Lock()。
程序仅通过等待组确保了顺序:每对goroutine在下一对启动之前都会完成。
英文:
The Go Language does not specify the order that goroutines execute outside of explicit synchronization using channels, mutexes, wait groups and so on. The specification allows for both outputs from both programs.
You are observing the order that the mutex is acquired, not the order that the goroutines start. It could be that the goroutines start in your expected order, but the unexpected goroutine calls Lock() first.
The only ordering ensured by the program is through the wait group: Each pair of goroutines will complete before the next pair is started.
答案2
得分: 0
Go协程提供并发性,并在独立的堆分配和栈内存上工作。它们的目的是在多核系统上提供并发执行。它们的并行执行方式没有统一的规定,而是由实际的处理器决定。我们只通过抽象层次来使用处理器。
实际上,没有一个并发系统可以控制/预测其结果。
英文:
The Go routines provide concurrency and work on top of share heap allocation with independent stack memory. The purpose of them is to provide concurrent execution on top of multi-core systems. There in no governing factor to how they are executed in parallel. This is decided by the actual processor. We only use the processor through layers of abstraction.
There is actually no concurrent system where you can govern/predict the outcome.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论