Go channels are a way to communicate between two goroutines. Reading data on a channel is simple, just use the receive operator:

ch := make(chan int)

go func() {
	fmt.Printf("%d\n", <-ch)
}()

ch <- 42

If you need to listen on several channels, you can use the select statement:

spawn := func() chan int {
	ch := make(chan int)

	go func() {
		ch <- rand.Intn(100)
	}()

	return ch
}

ch1 := spawn()
ch2 := spawn()

for i := 0; i < 2; i++ {
	select {
	case n := <-ch1:
		fmt.Printf("ch1: %d\n", n)

	case n := <-ch2:
		fmt.Printf("ch2: %d\n", n)
	}
}

However in some cases, you need to read values from multiple channels. For example, if you have multiple producers and a single consumer, you may want to merge several chans into a single one.

I encountered the problem when writing my IRC bot; the bot connects to multiple IRC servers, and each connection can produce events which are available on a channel. I needed a way to merge all these event streams into a single channel.

A simple and elegant solution is to use goroutines to read each channel and forward any data read to a single output channel.

In the following example, we will create several producers which emit random integers, then we will merge all the values into a single channel.

Let’s start by creating the producers:

spawnProducer := func(i int) chan int {
	ch := make(chan int)

	go func() {
		for {
			ch <- rand.Intn(100)
		}
	}()

	return ch
}

nbProducers := 3

inputs := make([]chan int, nbProducers)
for i := 0; i < nbProducers; i++ {
	inputs[i] = spawnProducer(i)
}

Then we create the output channel. Each value will be sent with the identifier of its producer. For my IRC bot, I needed to keep track of the server associated with each event.

type Value struct {
	Producer int
	Value    int
}

output := make(chan Value)

We can now spawn a goroutine for each producer, whose only job is to read the producer channel and write the wrapped value on the output channel:

for i := 0; i < nbProducers; i++ {
	go func(i int) {
		for {
			value := <-inputs[i]
			output <- Value{Producer: i, Value: value}
		}
	}(i)
}

It’s now trivial to consume the output channel:

for value := range output {
	fmt.Printf("%d: %d\n", value.Producer, value.Value)
	time.Sleep(250 * time.Millisecond)
}