Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

goroutines order of execution

Tags:

go

goroutine

I'm trying to understand this piece of code, not sure why the 2nd go is executed before the 1st one. It'd be great if someone can really help me out with this!

func sum(a []int, c chan int) {
   fmt.Println("summing: ", a)
   total := 0
   for _, v := range a {
      total += v
   }
   //fmt.Println("send to c",total)
   c <- total  // send total to c
}
func main() {
    //a := []int{7, 2, 8,134,23,23,1,23,1234,143, -9, 4, 0, 1234}

    c := make(chan int)

    go sum([]int{1,2,3}, c)
    go sum([]int{4,5,6}, c)

    x := <-c
    fmt.Println(x)
    x = <-c
    fmt.Println(x)
}

OUTPUT:

summing:  [4 5 6]
15
summing:  [1 2 3]
6
like image 330
jwtnb Avatar asked Oct 02 '16 14:10

jwtnb


People also ask

What order do Goroutines run in?

There is no defined order - they run concurrently. Which one happens to start first and/or finish first is random. Perhaps it helps if you don't think of the “go” keyword as “launching” a goroutine - see it as creating a goroutine data structure and putting it in the pool of runnable goroutines.

How do you wait for Goroutines to finish?

In order to be able to run the goroutines until the finish, we can either make use of a channel that will act as a blocker or we can use waitGroups that Go's sync package provides us with.

What happens to Goroutines when main exits?

When the main goroutine returns, the entire runtime system quits, rather abruptly. Hence any goroutines that are stuck waiting to send on an unbuffered or full channel simply ... cease to exist. They're not canceled, nor do they run, nor do they wait. Think of it as the flash paper being set on fire.

What are Goroutines and how do they work?

A goroutine is a lightweight execution thread in the Go programming language and a function that executes concurrently with the rest of the program. Goroutines are incredibly cheap when compared to traditional threads as the overhead of creating a goroutine is very low.


1 Answers

You have nothing explicitly synchronizing the order of the two goroutines. If you run this enough times, you will see the calls to fmt.Println print in different sequences. When executing goroutines, as they are concurrent operations, you have no guarantees when they will execute and/or complete. You need to use various standard library packages, or channels themselves to synchronize the execution of concurrently running goroutines.

For example (by leveraging the blocking nature of channels, you could do something like):

func main() {

    c := make(chan int)

    go sum([]int{1, 2, 3}, c)

    //use the channel to block until it receives a send
    x := <-c
    fmt.Println(x)

    //then execute the next routine
    go sum([]int{4, 5, 6}, c)

    x = <-c
    fmt.Println(x)
}

Another example (significantly less practical, but here to look at other common go synchronization features) you could introduce a wait group, and a range over a channel:

func sum(a []int, c chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("summing: ", a)
    total := 0
    for _, v := range a {
        total += v
    }
    //fmt.Println("send to c",total)
    c <- total // send total to c
}

func main() {

    c := make(chan int)
    wg := new(sync.WaitGroup)

    //concurrently call the concurrent calls to sum, allowing execution to continue to the range of the channel 
    go func() {
        //increment the wait group, and pass it to the sum func to decrement it when it is complete
        wg.Add(1)
        go sum([]int{1, 2, 3}, c, wg)
        //wait for the above call to sum to complete
        wg.Wait()
        //and repeat...
        wg.Add(1)
        go sum([]int{4, 5, 6}, c, wg)
        wg.Wait()
        //all calls are complete, close the channel to allow the program to exit cleanly 
        close(c)
    }()

    //range of the channel
    for theSum := range c {
        x := theSum
        fmt.Println(x)
    }

}
like image 148
syllabix Avatar answered Sep 29 '22 09:09

syllabix