Hello Ian, all, yes, the workers generate multiple results. I was able to use your proposal with the waiting goroutine which closes the channel. Unfortunately my initial minimal example was not so minimal. It is a little more complicated, as I have multiple "result" channels with different types and I don't know how many values I get. So I can't just range over one channel but instead use select with the ok from "val, ok := <-channel" to count the closed channels which finally ends the loop. Below is what I came up with (using Johns approach to run it multiple times) and it seems to be deterministic. Many thanks! PS: with buffered channels I have to disable closed channels with "channel = nil" to make it work.
package main import ( "fmt" "sync" ) func do(fc chan<- int, rc chan<- string) { fc <- 42 fc <- 43 rc <- "foo" } func main() { w := 4 r := 100000 n := 0 for i := 0; i < r; i++ { n = n + run(w) } fmt.Printf("Got %d, expected %d\n", n, 3*w*r) } func run(worker int) int { fc := make(chan int) rc := make(chan string) var wg sync.WaitGroup wg.Add(worker) for i := 0; i < worker; i++ { go func() { defer wg.Done() do(fc, rc) }() } go func() { wg.Wait() close(fc) close(rc) }() n := 0 closed := 0 for closed < 2 { select { case _, ok := <-fc: if ok { n++ } else { closed++ } case _, ok := <-rc: if ok { n++ } else { closed++ } } } return n } Am Di., 18. Dez. 2018 um 15:50 Uhr schrieb Ian Lance Taylor <i...@golang.org >: > On Tue, Dec 18, 2018 at 5:35 AM Chris Burkert <burkert.ch...@gmail.com> > wrote: > > > > I have a couple of goroutines sending multiple results over a channel - > a simple fan-in. They signal the completion on a done channel. Main selects > on the results and done channel in parallel. As the select is random main > sometimes misses to select the last result. What would be the idiomatic way > to prevent this and completely drain the result channel? > > > > Here is a minmal example which sometimes prints one 0 but should always > print two of them: > > > > package main > > > > import ( > > "fmt" > > ) > > > > func do(rc chan<- int, dc chan<- bool) { > > rc <- 0 > > dc <- true > > } > > > > func main() { > > worker := 2 > > rc := make(chan int, worker) > > done := 0 > > dc := make(chan bool, worker) > > for i := 0; i < worker; i++ { > > go do(rc, dc) > > } > > for done < worker { > > select { > > case <-dc: > > done++ > > case r := <-rc: > > fmt.Println(r) > > } > > } > > } > > > I assume the workers can generate multiple results, as otherwise the > done marker seems pointless. In general the simplest way to signal > completion on a channel is to call close. The simplest way to call > close on a fan-in is to have another goroutine that waits for the > other goroutines and closes the channel. That might look like > > package main > > import ( > "fmt" > "sync" > ) > > func do(rc chan<- int) { > rc <- 0 > } > > func main() { > worker := 2 > rc := make(chan int, worker) > var wg sync.WaitGroup > wg.Add(worker) > for i := 0; i < worker; i++ { > go func() { > defer wg.Done() > do(rc) > }() > } > go func() { > wg.Wait() > close(rc) > }() > for r := range rc { > fmt.Println(r) > } > } > > Ian > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.