我写了一个小程序练习go频道。
package main
import (
"log"
"strconv"
)
var MaxOutstanding int = 1
var channelSize int = 10
var sem = make(chan int, MaxOutstanding)
type Request struct {
command string
data string
}
func process(req *Request) {
log.Println(req)
}
func serve(reqs chan *Request) {
for req := range reqs {
sem <- 1
go func() {
process(req)
<-sem
}()
}
}
func main() {
reqs := make(chan *Request, channelSize)
for i := 0; i < channelSize; i++ {
req := &Request{"start", strconv.Itoa(i)}
reqs <- req
}
close(reqs)
serve(reqs)
}
此打印
2018/12/02 16:52:30 &{start 1}
2018/12/02 16:52:30 &{start 2}
2018/12/02 16:52:30 &{start 3}
2018/12/02 16:52:30 &{start 4}
2018/12/02 16:52:30 &{start 5}
2018/12/02 16:52:30 &{start 6}
2018/12/02 16:52:30 &{start 7}
2018/12/02 16:52:30 &{start 8}
2018/12/02 16:52:30 &{start 9}
因此,不会打印&{start 0}。为什么会丢失?
答案 0 :(得分:4)
因为在serve()
中,循环变量用于在单独的goroutine上执行的函数文字中,该变量由运行循环的goroutine同时修改:数据竞争。如果存在数据争用,则行为是不确定的。
如果您复制变量,它将起作用:
for req := range reqs {
sem <- 1
req2 := req
go func() {
process(req2)
<-sem
}()
}
在Go Playground上尝试。
另一种可能性是将其作为参数传递给匿名函数:
for req := range reqs {
sem <- 1
go func(req *Request) {
process(req)
<-sem
}(req)
}
在Go Playground上尝试这个。
这在几个相关的问题中有详细说明:
Using Pointers in a for Loop - Golang
Golang: Register multiple routes using range for loop slices/map
Why do these two for loop variations give me different behavior?
正如Zan Lynx指出的那样,您的主goroutine不会等待所有启动的goroutine完成,因此您可能看不到所有已打印的请求。看到这个问题,您如何才能等待启动的goroutine:Prevent the main() function from terminating before goroutines finish in Golang
答案 1 :(得分:1)
这是因为当匿名执行时,请求要求已从Request{0}
移至Request{1}
,因此您从{start 1}
打印。
// method 1: add a parameter, pass it to anonymous function
func serve(reqs chan *Request) {
for req := range reqs {
sem <- 1
// You should make the req as parameter of this function
// or, when the function execute, the req point to Request{1}
go func(dup *Request) {
process(dup)
<-sem
}(req)
}
// And you should wait for the Request{9} to be processed
time.Sleep(time.Second)
}
// method 2: make for wait anonymous function
func serve(reqs chan *Request) {
for req := range reqs {
go func() {
process(req)
sem <- 1
}()
// Or wait here
<-sem
}
}