Search code examples
goconcurrencychannelgoroutine

Is it possible to safely close a channel once from multiple goroutines using select?


My solution would be as follows, but is there a race condition in the following code (will it ever panic)?

c := make(chan struct{})

for i := 0; i < 1000000; i++ {
    go func() {
        select {
        case <-c:
        default:
            close(c)
        }
    }()
}

I would assume yes, but go test -race doesn't detect it and empirically I couldn't get it to panic.


Solution

  • You launch multiple goroutines without synchronization. So they run concurrently. It may happen 2 of them evaluates <-c in parallel, seeing it's a blocking operation, so both will choose the default case. At that point, both of those 2 goroutines will try to close the channel, and only one of them will succeed, the other will panic: you can't close a closed channel.

    This is not a race condition because you don't read/modify variables concurrently, but attempting to close a closed channel is a runtime panic (so the race detector will never catch this).

    When there are multiple goroutines sending on a channel, wait for all to complete and then close the channel in a single goroutine. Do not attempt to close a channel from multiple goroutines.