Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Priority in Go select statement workaround

I wish to have a go routine listening on two channels, blocked when both channels are drained. However, if both channels contains data, I want one to be drained before the other is handled.

In the working example below I wish all out to be drained before exit is handled. I use a select-statement which doesn't have any priority order. How might I get around the problem, making all 10 out-values be handled before the exit?

package main  import "fmt"  func sender(out chan int, exit chan bool){     for i := 1; i <= 10; i++ {         out <- i     }      exit <- true }  func main(){     out := make(chan int, 10)     exit := make(chan bool)      go sender(out, exit)      L:     for {         select {             case i := <-out:                 fmt.Printf("Value: %d\n", i)             case <-exit:                 fmt.Println("Exiting")                 break L         }     }     fmt.Println("Did we get all 10? Most likely not") } 
like image 795
ANisus Avatar asked Jun 20 '12 10:06

ANisus


1 Answers

The language supports this natively and no workaround is required. It's very simple: the quit channel should only be visible to the producer. On quit, the producer closes the channel. Only when the channel is empty and closed does the consumer quit. This is made possible by ranging over the channel.

Here is an example to illustrate:

package main  import (     "fmt"     "math/rand"     "time" )  var (     produced  = 0     processed = 0 )  func produceEndlessly(out chan int, quit chan bool) {     defer close(out)     for {         select {         case <-quit:             fmt.Println("RECV QUIT")             return         default:             out <- rand.Int()             time.Sleep(time.Duration(rand.Int63n(5e6)))             produced++         }     } }  func quitRandomly(quit chan bool) {     d := time.Duration(rand.Int63n(5e9))     fmt.Println("SLEEP", d)     time.Sleep(d)     fmt.Println("SEND QUIT")     quit <- true }  func main() {     vals, quit := make(chan int, 10), make(chan bool)     go produceEndlessly(vals, quit)     go quitRandomly(quit)     for x := range vals {         fmt.Println(x)         processed++         time.Sleep(time.Duration(rand.Int63n(5e8)))     }     fmt.Println("Produced:", produced)     fmt.Println("Processed:", processed) } 
like image 144
jorelli Avatar answered Sep 21 '22 08:09

jorelli