// It is saved as main.go
func main() {
arr := []int{1, 2, 3, 4, 5}
c := make(chan int, 5)
fin := make(chan int, 3)
cl := make(chan int, 1)
wg := new(sync.WaitGroup)
var once = new(sync.Once)
defer close(cl)
wg.Add(1)
go func() {
for _, ch := range arr {
c <- ch
}
close(c)
wg.Done()
}()
wg.Add(3)
for i := 0; i < 3; i++ {
go func() {
for {
if classId, ok := <-c; ok {
// time.Sleep(time.Second)
fin <- classId
} else {
break
}
}
once.Do(func() { close(fin) })
wg.Done()
}()
}
wg.Add(1)
go func() {
defer wg.Done()
for classId := range fin {
fmt.Printf("classId %d finished \n", classId)
}
cl <- 1
}()
cl <- 1
<-cl
wg.Wait()
fmt.Println("all done")
}
When we compile and run the program with the following command,
go build -race main.go
./main
we find the program is error.
classId 1 finished
classId 2 finished
classId 3 finished
panic: send on closed channel
And the error stack tells us that the line code 'fin - classId' is the root cause. Why?
This is because multiple goroutines are writing to the channel fin
and closing it at the same time. When the channels are ready, you can use a select to select one of them. On the receiving side you can use a select to pick from each channel when they are ready.
You can have multiple channels instead of having multiple writers to the same channel.
func main() {
arr := []int{1, 2, 3, 4, 5}
c := make(chan int, 5)
fin := make(chan int, 3)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for _, ch := range arr {
c <- ch
}
close(c)
wg.Done()
}()
wg.Add(3)
for i := 0; i < 3; i++ {
go func() {
defer wg.Done()
for classId := range c {
fin <- classId
}
}()
}
go func() {
wg.Wait()
close(fin)
}()
for classId := range fin {
fmt.Printf("classId %d finished \n", classId)
}
fmt.Println("all done")
}
In this modification, we added a new goroutine in the main function, which will wait for the completion of all the goroutines that are reading from the channel c and writing to the channel fin before closing the channel fin. By doing this, we are making sure that all the goroutines have finished reading from channel c and writing to channel fin, and thus avoiding any race conditions.
It's always a good practice to test the code with sample inputs and expected outputs to confirm it works as expected.