英文:
golang buffer channel unexpected result
问题
以下是翻译好的代码:
package main
import (
"time"
"runtime"
)
var c = make(chan int, 2)
func main() {
go worker(1)
for i := 0; i < 30; i++ {
go func() {
// k := i make a local copy not make any difference
c <- i
}()
}
time.Sleep(100* time.Second)
}
func worker(id int) {
for {
a := <-c
println(a, runtime.NumGoroutine())
time.Sleep(time.Second)
}
}
输出是不可预测的,有时候像下面这样:
7 9
13 29
13 28
13 27
13 26
13 25
13 24
13 23
16 22
16 21
17 20
19 19
21 18
21 17
23 16
25 15
26 14
26 13
26 12
26 11
26 10
26 9
26 8
26 7
27 6
27 5
13 4
28 3
30 2
30 2
我知道如果缓冲通道已满,发送者将被阻塞,当通道可用时,发送者可以继续发送。
- 为什么输出不是常量输出0-29?如何使其成为常量输出?
- 变量/局部变量在goroutine中如何存储?
- 如果有很多发送者被阻塞,它们按照FIFO顺序被唤醒吗?
英文:
package main
import (
"time"
"runtime"
)
var c = make(chan int, 2)
func main() {
go worker(1)
for i := 0; i < 30; i++ {
go func() {
// k := i make a local copy not make any difference
c <- i
}()
}
time.Sleep(100* time.Second)
}
func worker(id int) {
for {
a := <-c
println(a, runtime.NumGoroutine())
time.Sleep(time.Second)
}
}
the output is unpredictable, sometimes like below.
7 9
13 29
13 28
13 27
13 26
13 25
13 24
13 23
16 22
16 21
17 20
19 19
21 18
21 17
23 16
25 15
26 14
26 13
26 12
26 11
26 10
26 9
26 8
26 7
27 6
27 5
13 4
28 3
30 2
30 2
I know sender will block if the buffer channel is full, and when channel is available the sender can continue.
- why output is not constant output 0-29? how to make it??
- how variable/local variable store in goroutine??
- if lots of senders are blocked, are they waked up by FIFO order??
答案1
得分: 2
输出不是常量,因为不同的goroutine共享相同的局部变量i。如果你取消注释你的那一行,并将其移到goroutine调用之前,你会看到常量输出0-29。更好的方法是将i变量移到goroutine函数的参数中。
唤醒顺序在规范中没有指定。你应该将其视为随机的。
英文:
Output is not constant because different goroutines share same local variable i. If you uncomment your line and move it right before the goruoutine call, you'll see constant output 0-29. The better way is to move i variable to goroutine function arguments.
Wake up order is not specified in specs. You should consider it as a random one.
答案2
得分: 0
3 这是先进先出(FIFO)。
1 因为在for循环内创建的goroutine不一定按顺序执行。Go调度器会随机启动一个(这是通道分发值的方式)。当然,它们都会被创建,但它们会在main
函数中的time.Sleep(...)
调用的时候(Go调度器是协作式的,在函数调用、通道操作等特定点上进行调度,例如这里)开始(被调度)。
2 直接使用通道:
var (
c = make(chan int, 2)
wg = &sync.WaitGroup{}
)
func main() {
wg.Add(1)
go worker(1)
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 30; i++ {
c <- i
}
close(c)
}()
wg.Wait()
}
func worker(id int) {
defer wg.Done()
for a := range c {
println(a, runtime.NumGoroutine())
time.Sleep(time.Second)
}
}
关于传递for
循环变量,你几乎做得正确。你只需要将创建局部闭包的行放在goroutine之外:
var (
wg = &sync.WaitGroup{}
)
func main() {
for i := 0; i < 3; i++ {
localClosure := i // <- 这一行
wg.Add(1)
go func() {
defer wg.Done()
println(localClosure)
}()
}
wg.Wait()
}
英文:
3 It is FIFO
1 Because the goroutines created inside the for loop will not necessarily execute sequentially. The underlying Go scheduler will start one randomly (it's how channels dispatch their values). Of-course all of them will get created, but they will (get scheduled to) start at the point time.Sleep(...)
in main
is called (Go scheduler is a cooperative one and does so on certain points like function calls, channel ops, etc - for example this)
2 Use the channel directly:
var (
c = make(chan int, 2)
wg = &sync.WaitGroup{}
)
func main() {
wg.Add(1)
go worker(1)
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 30; i++ {
c <- i
}
close(c)
}()
wg.Wait()
}
func worker(id int) {
defer wg.Done()
for a := range c {
println(a, runtime.NumGoroutine())
time.Sleep(time.Second)
}
}
About passing a for
loop variable; you've done it almost correctly. You just have to put the line to create the local closure, outside the goroutine:
var (
wg = &sync.WaitGroup{}
)
func main() {
for i := 0; i < 3; i++ {
localClosure := i // <- this line
wg.Add(1)
go func() {
defer wg.Done()
println(localClosure)
}()
}
wg.Wait()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论