I wrote a small program to practice go channel.
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)
}
This prints
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}
Therefore, &{start 0} is not printed. Why is this missing?
Because in serve()
your loop variable is used inside the function literal you execute on a separate goroutine, which is concurrently modified by the goroutine running the loop: data race. If you have data race, the behavior is undefined.
If you make a copy of the variable, it will work:
for req := range reqs {
sem <- 1
req2 := req
go func() {
process(req2)
<-sem
}()
}
Try it on the Go Playground.
Another possibility is to pass this to the anonymous function as a parameter:
for req := range reqs {
sem <- 1
go func(req *Request) {
process(req)
<-sem
}(req)
}
Try this one on the Go Playground.
This is detailed in several related questions:
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?
Also as Zan Lynx noted, your main goroutine does not wait for all launched goroutines to complete, so you may not see all the requests printed. See this question how you can wait started goroutines: Prevent the main() function from terminating before goroutines finish in Golang