This program simply calculates something in separate goroutines and the main goroutine finally terminates after all the goroutines are finished. The question i wanted to ask was, is the way of quitting the infinite for loop in the printChannels() an acceptable one? Or does anyone has a better approach to doing this?package concurrencyimport (    goreceipes/concurrency/syncutils    fmt    math)// create a struct to hold the number and it's processed valuetype NumberObject struct {    number int    value  int}type QuitObject struct {    channelName string    quitValue int}// Mainfunc main() {    // notify the main of 4 threads in play    syncutils.Wg.Add(4)    // create channels for each function    squareCh := make(chan NumberObject)    fibCh := make(chan NumberObject)    dblCh := make(chan NumberObject)    quitCh := make(chan QuitObject, 3)    // launch threads to calculate values    go calculateSquares(squareCh, quitCh)    go calculateFibonacci(fibCh, quitCh)    go calculateDouble(dblCh, quitCh)    // launch the printer thread    go printChannels(squareCh, fibCh, dblCh, quitCh)    // wait for threads to complete    syncutils.Wg.Wait()    fmt.Println(Terminating program.)}// print the output of each channelfunc printChannels(sqCh <-chan NumberObject, fibCh <-chan NumberObject, dblCh <-chan NumberObject, quitCh <- chan QuitObject) {    // let the 'main' know i'm done    defer syncutils.Wg.Done()    // maintains a count of how many channels are exhausted    channelMap := make(map[string]int)    // initialize with zero. After each goroutine is done executing, they will send a 1 into the map as a quit signal    channelMap[sqCh] = 0    channelMap[fibCh] = 0    channelMap[dblCh] = 0    for {        select {        case obj := <- sqCh:            fmt.Printf(Square of %d = \t%d\n, obj.number, obj.value)        case obj := <- fibCh:            fmt.Printf(Fibonacci of %d = %d\n, obj.number, obj.value)        case obj := <- dblCh:            fmt.Printf(Double of %d = \t%d\n, obj.number, obj.value)        case val := <- quitCh:            channelMap[val.channelName] = val.quitValue            if channelMap[sqCh] == 1 && channelMap[fibCh] == 1 && channelMap[dblCh] == 1{                fmt.Println(All channels are done executing. Break the infinite loop)                return            }        }    }}// calculates doublefunc calculateDouble(dblCh chan<- NumberObject, quitCh chan <- QuitObject) {    defer syncutils.Wg.Done()    for i := 0; i < 10; i++ {        dblCh <- NumberObject{number: i, value: i * 2}    }    // send the quit signal    quitCh <- QuitObject{dblCh, 1}}// calculate fibonaccifunc calculateFibonacci(fibCh chan<- NumberObject, quitCh chan <- QuitObject) {    // let the main know I'm done    defer syncutils.Wg.Done()    for i := 0; i < 10; i++ {        num := float64(i)        Phi := (1 + math.Sqrt(num)) / 2        phi := (1 - math.Sqrt(num)) / 2        result := (math.Pow(Phi, num) - math.Pow(phi, num)) / math.Sqrt(5)        fibCh <- NumberObject{number: int(num), value: int(result)}    }    // send the quit signal    quitCh <- QuitObject{fibCh, 1}}// calculates squaresfunc calculateSquares(sqCh chan<- NumberObject, quitCh chan <- QuitObject) {    // let the main know I'm done    defer syncutils.Wg.Done()    for i := 0; i < 10; i++ {        sqCh <- NumberObject{number: i, value: i * i}    }    // send the quit signal    quitCh <- QuitObject{sqCh, 1}}and syncutils.Wgis defined in a separate package so it can be used whereever requiredpackage syncutilsimport syncvar Wg sync.WaitGroup/**    the purpose of this file/code is to only provide a global variable Wg = WaitGroup and package it    so it can be included wherever needed. */Can somebody suggest a better way to terminate an infinite loop which is reading multiple channels in a different goroutine?Thanks in advance for the help.
Communicating across channels golang
go
You can do without the chan QuitObject:As you mention, the main issue is to quit the printChannels properly. Since it is listening on 3 channels, it is quite complicated to achieve.To solve this, I recommend adding a chan string which would (concurrently) receive the string to print from the 3 others channels and close once they are done.The printChannels becomes printResult (it could actually be directly written in the main): func printResult(in <-chan string) {    for s := range in { // this loop will end once the input channel is closed        fmt.Println(s)    }}To feed this channel, we need goroutines which take a result and create a string:func formatResult(in <-chan NumberObject, out chan<- string, format string) {    for n := range in { // this loop will end once the input channel is closed        out <- fmt.Sprintf(format, n.number, n.value)    }}// usage:resultCh := make(chan string)go formatResult(squareCh, resultCh, Square of %d = \t%d)go formatResult(fibCh, resultCh, Fibonacci of %d = %d)go formatResult(dblCh, resultCh, Double of %d = \t%d)// close the resultCh at the endWe actually just moved the issue : we now need to know when to close the resultCh!The WaitGroup make sense here (a local variable is all that we need):var wg = sync.WaitGroup{}wg.Add(3)go func() {    formatResult(squareCh, resultCh, Square of %d = \t%d)    wg.Done()}()go func() {    formatResult(fibCh, resultCh, Fibonacci of %d = %d)    wg.Done()}()go func() {    formatResult(dblCh, resultCh, Double of %d = \t%d)    wg.Done()}()go func() {    // wait for threads to complete    wg.Wait()    resultCh <- All channels are done executing.    close(resultCh)}()But as-is, the formatResult never finish, because the input channel is never closed. So we need to modify the computation functions to close the channel after the for loop.// calculates squaresfunc calculateSquares(sqCh chan<- NumberObject) {    for i := 0; i < 10; i++ {        sqCh <- NumberObject{number: i, value: i * i}    }    close(sqCh)}As a further refinement, you could change the computation functions to be just func(int)int and add helper functions to call them and fill the expected channel:func calculate(f func(int) int, ch chan<- NumberObject) {    for i := 0; i < 10; i++ {        ch <- NumberObject{number: i, value: f(i)}    }    close(ch)}Final program (I integrated the wg.Done inside a calculateAndFormat to reduce verbosity, but it reduces flexibility a bit):package mainimport (    fmt    math    sync)func main() {    resultCh := make(chan string)    var wg = sync.WaitGroup{}    wg.Add(3)    // launch threads to calculate values    go calculateAndFormat(square, resultCh, Square of %d = \t%d, &wg)    go calculateAndFormat(fibonacci, resultCh, Fibonacci of %d = %d, &wg)    go calculateAndFormat(double, resultCh, Double of %d = \t%d, &wg)    go func() {        wg.Wait()        resultCh <- All channels are done executing.        close(resultCh)    }()    for s := range resultCh {        fmt.Println(s)    }    fmt.Println(Terminating program.)}func calculateAndFormat(f func(int) int, ch chan<- string, format string, wg *sync.WaitGroup) {    for i := 0; i < 10; i++ {        ch <- fmt.Sprintf(format, i, f(i))    }    wg.Done()}// calculates doublefunc double(i int) int {    return i * 2}// calculate fibonaccifunc fibonacci(i int) int {    num := float64(i)    Phi := (1 + math.Sqrt(num)) / 2    phi := (1 - math.Sqrt(num)) / 2    result := (math.Pow(Phi, num) - math.Pow(phi, num)) / math.Sqrt(5)    return int(result)}// calculates squaresfunc square(i int) int {    return i * i}