Just realized what you meant about buffered channels. I'm wrong, and sorry
for the confusion.

+1 to your original solution (combined with setting channels to nil).

Diane Looney


On Tue, Dec 18, 2018 at 11: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.
>
> 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