Reading from multiple channels simultaneously in Golang

31,234

Solution 1

Depending on your requirements, you may need to read both of the channels for every iteration (i.e. a sort-of 'zip' function). You can do this with a select, similarly to user860302's answer:

func main() {

  c1 := make(chan int)
  c2 := make(chan int)
  out := make(chan int)

  go func(in1, in2 <-chan int, out chan<- int) {
    for {
      sum := 0
      select {
      case sum = <-in1:
        sum += <-in2

      case sum = <-in2:
        sum += <-in1
      }
      out <- sum
    }
  }(c1, c2, out)
}

This runs forever. My preferred way to terminate goroutines like this one is to close the input channels. In this case you would need to wait for both to close, then close(out) before terminating.

Tip: note the use of directional channels as goroutine formal parameters. The compiler catches more mistakes when you write it this way. Happiness!

Solution 2

The simplest answer would be

func addnum(num1, num2, sum chan int) {
  n1 := <- num1
  n2 := <- num2
  sum <- n1 + n2
}

Since you need both num1 and num2 to do the calculation, it makes no sense to do it otherwise. After all, there are two possible execution orders:

  1. num1 generates a number, followed by num2
  2. num2 generates a number, followed by num1

In the first case, our channel reads correspond exactly to the execution order. In the second case, our first read will block until num1 has finally produced a number; the second read will complete near-instantaneous because the num2 channel already has a number.

If you want to know more about channels in Go, I'd suggest to have a look at http://godoc.org/github.com/thomas11/csp -- this is a collection of Hoare's CSP examples written in Go.

Solution 3

To answer the question "Reading from multiple channels simultaneously"

There is a way to listen to multiple channels simultaneously :

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    ...
    go func() {
        for {
            select {
                case msg1 := <- c1:
                fmt.Println(msg1)

                case msg2 := <- c2:
                fmt.Println(msg2)
             }
        }
    }()

In this example, I create a channel msg1 and msg2. Then I create a go routine with an infinite loop. In this loop, I listen to msg1 AND msg2. This system allow you to read to multiple channels simultaneously and to process the messages when the arrive.

In order to avoid leaks, I should probably add another channel to stop the goroutine.

Share:
31,234
Jing
Author by

Jing

Updated on July 09, 2022

Comments

  • Jing
    Jing almost 2 years

    I am new to Golang. Right now I am trying to figure out how to make an any-to-one channel in Golang, where the setup is as follows:

    say I have two goroutines numgen1 and numgen2 executing concurrently and writing numbers to channels num1 resp. num2. I would like to add the numbers sent from numgen1 and numgen2 in a new process, addnum. I have tried something like this:

    func addnum(num1, num2, sum chan int) {
        done := make(chan bool)
        go func() {
            n1 := <- num1
            done <- true
        }()
            n2 := <- num2
            <- done
        sum <- n1 + n2
    }
    

    but this seems sadly incorrect. Could someone please give me some ideas?

    Thank you very much for your help.

  • Jing
    Jing over 10 years
    Thanks :) If I want to let the channel read continuously, can I write two for loops for range num1 and range num2 respectively?
  • publysher
    publysher over 10 years
    No, because this will first fully consume range num1 (possibly never) before it will read the first entry from range num2. You can wrap the entire function body in a for { ... } loop to keep calculating.
  • Javier Neyra
    Javier Neyra over 10 years
    this is not reading simultanously, this is checking simultanuosly and reading when channel is ready.. so, in realworld, you will need syncronization code.
  • zk82
    zk82 over 10 years
    Notice that you used c1 and c2 in the inner function instead of in1 and in2.
  • zk82
    zk82 over 10 years
    Hi, it's nice, specially you in/out comment but notice that you used c1 and c2 in the inner function instead of in1 and in2. Also you are hiding sum in the select so it is 0 when you send it to out. Sorry for double-posting.
  • Rick-777
    Rick-777 over 10 years
    Thanks for that correction - I've updated the code as you suggested.
  • Rick-777
    Rick-777 over 10 years
    Please also compare this suggestion with that from @publysher, which is a simpler way of achieving a similar thing. His suggestion will be fine in cases where the potentially-longer blocking of other goroutines doesn't matter and doesn't increase the risk of deadlock. If in doubt, my strategy listed above is the safer choice.
  • Vlad the Impala
    Vlad the Impala over 9 years
    this seems easier than the accepted answer, and it is "correct enough".