Panic: send on closed channel

package main

import (
“fmt”
“math/rand”
“sync”
)

func drG(wg *sync.WaitGroup, c chan int) func(int, int, int) {

return func(min, max, itr int) {
	wg.Add(1)
	defer wg.Done()
	for i := 0; i <= itr; i++ {
		c <- rand.Intn(max-min) + max
	}
	close(c)
	wg.Wait()
}

}

func main() {
wg := sync.WaitGroup{}
c1, c2 := make(chan int), make(chan int)
ar_c1, ar_c2 := int{}, int{}

go drG(&wg, c1)(10, 99, 2)
go drG(&wg, c1)(111, 999, 3)
go drG(&wg, c2)(0, 9, 3)
for i := range c1 {
	ar_c1 = append(ar_c1, i)
}
for i := range c2 {
	ar_c2 = append(ar_c2, i)
}

fmt.Println(ar_c1, ar_c2)

}

Hello gophers.
I am very new in programming world and am doing this as a hobby. I have read on goroutines and channels and thought I’d give it a try only to come across this challenge

I am getting below error:

[166 187 121] [15 14 10 17]
panic: send on closed channel

goroutine 7 [running]:
main.main.drG.func2(0x6f, 0x3e7, 0x3)
/tmp/sandbox3358109023/prog.go:17 +0xb5
created by main.main in goroutine 1
/tmp/sandbox3358109023/prog.go:31 +0x19b

Program exited.

When I use goroutine with a single c2 channel, it’s working with no errors, on using two goroutines with same channels, I get the; panic: send on closed channel error.

How do I overcome this. Apologies if this may be a basic questions to you guys

Hi @Dj_Stata, welcome to the forum.

First, no need to apologize. Every question is welcome.
Second, a tip: Surround your code by a “code fence”, like so:

```go
package main

// more code...
``` 

The code is more readable this way and does not contain fancy quotes or checkboxes instead of [].

Regarding your question:

You start two goroutines that send to the same channel:

	go drG(&wg, c1)(10, 99, 2)
	go drG(&wg, c1)(111, 999, 3)

The goroutine that finishes first closes the channel. Writing on a closed channel is an error, so if the second goroutine still has something to write to the channel, the attempt fails.

Pro Tip: Memorize the four Channel Axioms.

1 Like

import (
	"fmt"
	"math/rand"
	"sync"
)

func drG(wg *sync.WaitGroup, c chan int) func(int, int, int) {

	return func(min, max, itr int) {
		wg.Add(1)
		defer wg.Done()
		for i := 0; i <= itr; i++ {
			c <- rand.Intn(max-min) + max
		}
		close(c)
		wg.Wait()
	}

}

func main() {
	wg := sync.WaitGroup{}
	c1, c2 := make(chan int), make(chan int)
	ar_c1, ar_c2 := []int{}, []int{}

	go drG(&wg, c1)(10, 99, 2)
	go drG(&wg, c1)(111, 999, 3)
	go drG(&wg, c2)(0, 9, 3)
	for i := range c1 {
		ar_c1 = append(ar_c1, i)
	}
	for i := range c2 {
		ar_c2 = append(ar_c2, i)
	}

	fmt.Println(ar_c1, ar_c2)

}

I really appreciate your feedback and I request you to take my code and edited it to run successfully

This, array chunkings , and recursion are really a challenge to wrap around

@Dj_Stata There are two issues to address.

Issue 1: Two goroutines send into the same channel. Hence neither of them can close the channel once ready, because the other goroutine would then write to a closed channel and panic.

Solution 1: use a separate “done” channel to signal that a goroutine is done. The receiving end only needs to count the number of “dones” sent through this channel. If 3 goroutines were spawned, the receiver has to wait for three “dones”.

Solution 2: Instead of ranging over the channel, range over the known number of generated values. The number is known because the function returned by drG receives an itr parameter determining the number of iterations.

Issue 2: Unbuffered channels create a synchronization situation: The receiver must wait until the sender sends an item to the channel.

A possible solution: Use buffered channels, but be aware that merely adding a buffer often just delays the problem.

Issue 3: The main goroutine has to iterate over two output channels c1 and c2, but the second loop must wait until the first one finishes.

A possible solution: Either make all goroutines send to the same channel and use a single loop to collect the results, or read the result channels concurrently. The latter approach would require another goroutine and a way to safely write to ar_c1, which can easily becomes a rabbit hole.

But…

The main question is: What do you want to achieve in the end?

If the goal is to concurrently generate several slices of random values, how about having each goroutine fill a slice and pass the complete slice back through a result channel?

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.