Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

go

channel

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 (     "fmt"     "runtime"     "sync"     "time" )  // 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])             wg.Done()         }(i)     }      // Launch controller routine.     go func() {         controller(workers)         wg.Done()     }()      // Wait for all goroutines to finish.     wg.Wait() }  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)                 return             case Running:                 fmt.Printf("Worker %d: Running\n", id)             case Paused:                 fmt.Printf("Worker %d: Paused\n", id)             }          default:             // 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.             runtime.Gosched()              if state == Paused {                 break             }              // 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.     <-time.After(1e9)     for i := range workers {         workers[i] <- Paused     }      // Unpause workers.     <-time.After(1e9)     for i := range workers {         workers[i] <- Running     }      // Shutdown workers.     <-time.After(1e9)     for i := range workers {         close(workers[i])     } } 

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.

like image 635
Reck Hou Avatar asked Apr 19 '13 13:04

Reck Hou


People also ask

What happens when a channel is closed?

Closing a channel indicates that no more values will be sent on it. This can be useful to communicate completion to the channel's receivers.

What happens when channel is closed Golang?

We can close a channel in Golang with the help of the close() function. Once a channel is closed, we can't send data to it, though we can still read data from it. A closed channel denotes a case where we want to show that the work has been done on this channel, and there's no need for it to be open.

How do I gracefully close a channel?

The Channel Closing Principle One general principle of using Go channels is don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders. In other words, we should only close a channel in a sender goroutine if the sender is the only sender of the channel.

Do I need to close a channel?

You needn't close every channel when you've finished with it. It's only necessary to close a channel when it is important to tell the receiving goroutines that all data have been sent.


2 Answers

There's no way to write a safe application where you need to know whether a channel is open without interacting with it.

The best way to do what you're wanting to do is with two channels -- one for the work and one to indicate a desire to change state (as well as the completion of that state change if that's important).

Channels are cheap. Complex design overloading semantics isn't.

[also]

<-time.After(1e9) 

is a really confusing and non-obvious way to write

time.Sleep(time.Second) 

Keep things simple and everyone (including you) can understand them.

like image 176
Dustin Avatar answered Oct 11 '22 11:10

Dustin


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) (example)
  • 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.

like image 31
zzzz Avatar answered Oct 11 '22 09:10

zzzz