DEV Community

sai teja
sai teja

Posted on Edited on

Handle go routines gracefully

Understanding and handling the Go routines is little bit tricky but once you got to know the cream of Go Routine it will be a cake walk of creating and handling the routines 👍
Updated the code with explanation
please go thru it and please feel free to add comments if i miss anything / you want to add something..!!

package main

import (
    "fmt"
    "sync"
    "time"
)

const maxRoutines = 5

func main() {
    HandleUsingWaitGroup()
    HandleUsingChannel()
}

func HandleUsingWaitGroup() {
    var wg sync.WaitGroup

    // add no of go routines to trigger
    wg.Add(maxRoutines)

    for i := 1; i <= maxRoutines; i++ {
        go waitGroupRoutine(&wg, i)
    }

    // it will block the main until all go routines run successfully
    // whenever wg.Done() informs wait group it will reduce the go routine count by 1
    // and it will block until it becomes zero(0)
    wg.Wait()
}
func waitGroupRoutine(wg *sync.WaitGroup, value int) {

    // this will inform  wait group once it is done with the task
    defer wg.Done()

    fmt.Println("i am from waitGroup routine ", value)

}

// this works with buffered and unbuffered channels
func HandleUsingChannel() {
    ch := make(chan int, maxRoutines+1)

    //uncomment below line if you want to test un-buffered channel
    //ch := make(chan int)

    // this channel acts as a gatekeeper for the main func
    exitCh := make(chan bool)

    // this channel acts as a mediator to inform gatekeeper to exit main once all tasks are done
    mediatorCh:=make(chan bool)

    // trigger go routine and wait for the data in background
    go channelRoutine(exitCh,mediatorCh, ch)

    for i := 1; i <= maxRoutines; i++ {

        // push data to channel
        ch <- i

        if i == maxRoutines {

            // tell mediator to inform exit channel to exit the main func
            // and it return the go routine / exit the go routine
            mediatorCh <- true

            // close the channel here and read residual data in routine
            // else we will get a dead lock error because data push to channel is done
            // and nothing to read from it and we are trying to access the same...
            close(ch)
        }

    }

    // it will block the main until all go routines run successfully
    select {
    case <-exitCh:
        close(exitCh)
        fmt.Println("exit from Channel main")
        break

    }
}

func channelRoutine(exitCh chan bool,mediatorCh chan bool, ch <-chan int) {
    for {
        select {
        case <-mediatorCh:
            // we can read from closed channel
            close(mediatorCh)

            // read residual data before exiting
            // if we use buffered channel size of our data length
            //it will have enough space to push data to channel
            // once it pushes everything it will exit and routine will close automatically
            //and to avoid data miss we have to read channel data before returning
            for v := range ch {

                // this will show the difference in logs while comparing buffered and un-buffered channels
                time.Sleep(time.Second * 1)
                fmt.Println(" i am from  channel routine exit state", v)
            }

            // here we are telling exit channel to exit the main
            exitCh<-true
            return
        case x := <-ch:
            fmt.Println(" i am from channel routine", x)


        }
    }
}

Enter fullscreen mode Exit fullscreen mode

please follow medium for interesting things

Sai Teja – Medium

Read writing from Sai Teja on Medium. Software Engineer | Fitness trainer | Random Thinker. Every day, Sai Teja and thousands of other voices read, write, and share important stories on Medium.

favicon medium.com

Top comments (0)