On Wed, Dec 19, 2018, 5:31 AM Chris Burkert <burkert.ch...@gmail.com> wrote:

> 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.
>

You should need to disable the closed channel by setting it nil regardless
of it being buffered or not. Depending on how you actually end up sending
results back on multiple channels it could still be a bug if you end up
selecting on the closed channel multiple times and think you are done
instead of reading from another channel with results.


> 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.
>

-- 
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