Skip to content

Latest commit

 

History

History
123 lines (103 loc) · 2.64 KB

File metadata and controls

123 lines (103 loc) · 2.64 KB

Multiple Sender Send to A Channel

Error Code

// 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?

Root Cause

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.

Best Practice

You can have multiple channels instead of having multiple writers to the same channel.

Modification

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.