Home > OS >  select vs multiple concurrent coroutines receiving on different channels : Is there a difference in
select vs multiple concurrent coroutines receiving on different channels : Is there a difference in

Time:11-16

Select vs multiple concurrent coroutines receiving on different channels : Is there a difference in logic or in performance ?

My question is more generally about the implementation of an "fan-in" scheme in Go. It seems to me that the scheme using "select" does not work in the case of an arbitrarily large amount of channels (large array of channels).

See receive() and receive2() in the example hereunder.

Is the receive2() function overcomplicated? Overkill?

Why is the select formulation regarded as more idiomatic ?

package main

import (
    "fmt"
    "time"
)

func main() {

    var ch1 = make(chan int)
    var ch2 = make(chan int)

    send(ch1, ch2)
    //receive(ch1, ch2)
    receive2(ch1, ch2)

    time.Sleep(3 * time.Second)

}

func send(ch1 chan int, ch2 chan int) {
    go func() {
        for i := 0; i < 10; i   {
            ch1 <- i
        }
        close(ch1)
    }()
    go func() {
        for i := 0; i < 10; i   {
            ch2 <- i
        }
        close(ch2)
    }()
}

func receive(ch1 chan int, ch2 chan int) {
    go func() {
        for item := range ch1 {
            fmt.Printf("1: %d\n", item)
        }
    }()

    go func() {
        for item := range ch2 {
            fmt.Printf("2: %d\n", item)
        }
    }()
}

func receive2(ch1 chan int, ch2 chan int) {
    for {
        select {
        case x, ok := <-ch1:
            fmt.Println("ch1", x, ok)
            if !ok {
                ch1 = nil
            }
        case x, ok := <-ch2:
            fmt.Println("ch2", x, ok)
            if !ok {
                ch2 = nil
            }
        }

        if ch1 == nil && ch2 == nil {
            break
        }
    }
}

CodePudding user response:

Your implementation is not a fan-in. The fan-in collects the results from multiple channels into one channel. Something like this, where you can receive all values on the single out channel.

func fanIn(in ...chan int) chan int {
    out := make(chan int)
    for _, c := range in {
        go func(i chan int) {
            for v := range i {
                out <- v
            }

        }(c)
    }
    return out
}

In your implementation, the first receive() is simply receiving concurrently from N channels, independent of each other. It can be rewritten to accept a vararg and can look more like an actual fan-in:

func receive(cs ...chan int) {
    for i, cN := range cs {
        go func(i int, c chan int) {
            for item := range c {
                fmt.Printf("%d: %d\n", i, item)
            }
        }(i, cN)
    }
}

The receive2() instead is essentially sequential and won't scale, since you would have to write one case for each channel, and && them all together to know when to break the loop. The && may be rewritten with a sync.WaitGroup but you'll still process only one item at each iteration (at random, among the cases that are ready to receive).

  • Related