The proof that Go channels aid concurrency
Posted on Sat 01 July 2023 in IT, programming
All the buzz about Go being the best modern language to handle concurrency... When I rewrote the Neo4j Go Driver manual I needed to get to the bottom of it. And indeed, there is some truth. After going through A Tour of Go – Concurrency, I managed to stitch together the following example, where a bunch of routines concurrently process entries from a single channel. (The sender spawns a Goroutine, but it's not a requirement – it is here, because the channel is unbuffered and the sender would thus block until a receiver would start receiving; but if you'd make it buffered, then you wouldn't need a routine.)
package main
import (
"fmt"
"time"
)
// Produce a channel streaming integers
func sender() <-chan int {
out := make(chan int)
l := []int{1,2,3,4,5,6,7,8,9,10}
go func() {
for _, entry := range l {
out <- entry
}
close(out)
}()
return out
}
// Receivers listen on a channel and log on a separate one
func receiver(c <-chan int, log chan string, n int) {
go func() {
for entry := range c {
log <- fmt.Sprintf("Receiver %v processed %v", n, entry)
time.Sleep(time.Duration(n) * time.Second)
}
close(log)
}()
}
func main() {
fmt.Println("Start")
defer timer("Main")()
source := sender()
log := make(chan string)
receiver(source, log, 1) // processes with a 1 second delay
receiver(source, log, 2) // processes with a 2 seconds delay
for entry := range log {
fmt.Println(entry)
}
}
// Prints caller execution time
func timer(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}