Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do goroutines work?

I was following the Go Tour and I am a bit stuck when it comes to goroutines. I understand that they are very lightweight and that every time a goroutine blocks, another one will start but I can't get my head around how this example actually works:

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(1000 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}

Playground

I understand that a goroutine is started for the say function with the argument "world", but as far as I understand that should print "world" five times and "hello" once. However I don't understand why the output is as it is:

hello
world
hello
world
hello
world
hello
world
hello

From my limited understanding of threads from other languages the output should have been something like this:

hello
world
world
world
world
world

or like this:

world 
world
world
hello
world
world

Why does the second line execute five times as well? Does anything below a go statement classify as part of the go routine?

Also the next slide shows something I can't get my head round again:

package main

import "fmt"

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}

Playground

A goroutine is started for the second half of the slice and then another one for the first part of the slice, however the values x and y have been assigned two different values. The way I see it the sum function will send it's sum to channel c and then the next sum will send it's sum to the same channel c so how can the two variables be assigned two different values? Shouldn't channel c have one single sum value in there?

I appreciate that this is quite a long question but I wasn't able to find the answer to these questions.

like image 962
Bula Avatar asked Aug 28 '14 08:08

Bula


People also ask

How do Goroutines work internally?

A goroutine has a simple model: it is a function executing concurrently with other goroutines in the same address space. It is lightweight, costing little more than the allocation of stack space. And the stacks start small, so they are cheap, and grow by allocating (and freeing) heap storage as required.

When should you use Goroutines?

Goroutines are useful when you want to do multiple things simultaneously. For example, if you have ten things you want to do at the same time, you can do each one on a separate goroutine, and wait for all of them to finish.

How are Goroutines executed?

To invoke this function in a goroutine, use go f(s) . This new goroutine will execute concurrently with the calling one. You can also start a goroutine for an anonymous function call. Our two function calls are running asynchronously in separate goroutines now.

How Goroutines work under the hood?

Goroutines utilizes a concept that has been around for a while called “coroutines” which essentially means multiplexing a set of independently executing functions “coroutines” which are running on the user level, onto a set of actual threads on the OS level. That's what makes goroutines incredibly efficient.


1 Answers

Why does the second line execute 5 times as well?

The second line will print hello every second 5 times in the main() thread.
But concurrently the first line go say("world") will also print world every seconds five times in a separate goroutine.
The Sleep ensure that each routine yields, allowing the other to resume.

Hence the output:

hello
world
hello
world
hello
world
hello
world
hello

The way I see it the sum function will send it's sum to channel c and then the next sum will send it's sum to the same channel c so how can the two variables be assigned two different values?

Because each send will block on c until channel c is read.
Since there are two write to c, you need to read:

 x, y := <-c, <-c // receive from c twice.

The Assignement section of Golang Spec allows for a tuple assignment if:

the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left.

like image 54
VonC Avatar answered Sep 18 '22 14:09

VonC