How to check a channel is closed or not without reading it?

This is a good example of workers & controller mode in Go written by @Jimt, in answer to “Is there some elegant way to pause & resume any other goroutine in golang?

package main

import (

// Possible worker states.
const (
    Stopped = 0
    Paused  = 1
    Running = 2

// Maximum number of workers.
const WorkerCount = 1000

func main() {
    // Launch workers.
    var wg sync.WaitGroup
    wg.Add(WorkerCount + 1)

    workers := make([]chan int, WorkerCount)
    for i := range workers {
        workers[i] = make(chan int)

        go func(i int) {
            worker(i, workers[i])

    // Launch controller routine.
    go func() {

    // Wait for all goroutines to finish.

func worker(id int, ws <-chan int) {
    state := Paused // Begin in the paused state.

    for {
        select {
        case state = <-ws:
            switch state {
            case Stopped:
                fmt.Printf("Worker %d: Stopped\n", id)
            case Running:
                fmt.Printf("Worker %d: Running\n", id)
            case Paused:
                fmt.Printf("Worker %d: Paused\n", id)

            // We use runtime.Gosched() to prevent a deadlock in this case.
            // It will not be needed of work is performed here which yields
            // to the scheduler.

            if state == Paused {

            // Do actual work here.

// controller handles the current state of all workers. They can be
// instructed to be either running, paused or stopped entirely.
func controller(workers []chan int) {
    // Start workers
    for i := range workers {
        workers[i] <- Running

    // Pause workers.
    for i := range workers {
        workers[i] <- Paused

    // Unpause workers.
    for i := range workers {
        workers[i] <- Running

    // Shutdown workers.
    for i := range workers {

But this code also has an issue: If you want to remove a worker channel in workers when worker() exits, dead lock happens.

If you close(workers[i]) , next time controller writes into it will cause a panic since go can’t write into a closed channel. If you use some mutex to protect it, then it will be stuck on workers[i] <- Running since the worker is not reading anything from the channel and write will be blocked, and mutex will cause a dead lock. You can also give a bigger buffer to channel as a work-around, but it’s not good enough.

So I think the best way to solve this is worker() close channel when exits, if the controller finds a channel closed, it will jump over it and do nothing. But I can’t find how to check a channel is already closed or not in this situation. If I try to read the channel in controller, the controller might be blocked. So I’m very confused for now.

PS: Recovering the raised panic is what I have tried, but it will close goroutine which raised panic. In this case it will be controller so it’s no use.

Still, I think it’s useful for Go team to implement this function in next version of Go.

1 Like

In a hacky way it can be done for channels which one attempts to write to by recovering the raised panic. But you cannot check if a read channel is closed without reading from it.

Either you will

  • eventually read the “true” value from it ( v <- c )
  • read the “true” value and ‘not closed’ indicator ( v, ok <- c )
  • read a zero value and the ‘closed’ indicator ( v, ok <- c )
  • will block in the channel read forever ( v <- c )

Only the last one technically doesn’t read from the channel, but that’s of little use.

Refer: stackoverflow