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.

Reply via email to