This locks like a rather very basic problem regarding to channels. I have read that unbuffered channels block on send, so why this code has a race-condition?
Sometimes the output is
<- CREATE;
<- INSERT;
END
[CREATE; INSERT;]
Sometime the "INSERT;" is missing from the output, but it's written to the channel.
<- CREATE;
<- INSERT;
END
[CREATE; INSERT;]
How can the SplitChan
func return, if it blocks on cmd := <-c
from Slit
?
package main
import "fmt"
func main() {
batch := `CREATE;INSERT;`
res := Split(batch)
fmt.Print(res)
}
func SplitChan(batch string, outc chan<- string) {
b := 0
for i, c := range batch {
switch c {
case ';':
cmd := batch[b : i 1]
fmt.Println("<- " cmd)
b = i 1
outc <- cmd
}
}
fmt.Println("END")
}
func Split(batch string) []string {
var res []string
c := make(chan string)
go func() {
for {
cmd := <-c
res = append(res, cmd)
}
}()
SplitChan(batch, c)
close(c)
return res
}
Playground Link: https://go.dev/play/p/WmO5OtmgETl
I am expected the same output on every run:
<- CREATE;
<- INSERT;
END
[CREATE; INSERT;]
What am I missing here? Thank you
CodePudding user response:
When SplitChan
writes to the channel, the append that's happening at the goroutine and the statement return res
are concurrent. return res
may see the slice with one element or two elements.
You have to make sure that before returning, the append operation is completed:
wg:=sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for cmd := range c {
res = append(res, cmd)
}
}()
SplitChan(batch, c)
close(c)
wg.Wait()
return res
The WaitGroup ensures that the function returns after the goroutine is done.