DEV Community

Angel Dhakal
Angel Dhakal

Posted on • Updated on

Concurrency in Go - using goroutines

Goroutine

A goroutine is the built-in primitive in go that helps achieving concurrency. We can create a goroutine by simply adding go keyword before a function call, which tells the go runtime to spawn a new child process AKA goroutine and complete the execution of that function in a new memory space.

Fork-Join Model

Go follows the fork-join concurrency model in which a new goroutine is forked off(spawned) of main goroutine and main goroutine continues its task after spawning child goroutines. After a goroutine is forked off a main goroutine it does some task and it rejoins the main goroutine and the point where a goroutine is joined with the main goroutine is called join point. The responsibility of implementing of the join point rests to the developer. Let's see this in action with a simple example.

package main

import "fmt"

func sayHi(name string) {
    fmt.Printf("Hi %s\n", name)
}

func main() {
    fmt.Println("Program Started")

    go sayHi("Luffy")
    go sayHi("Zoro")
    go sayHi("Sanji")

    fmt.Println("Program Ended")
}
Enter fullscreen mode Exit fullscreen mode

Output

Program Started
Program Ended
Enter fullscreen mode Exit fullscreen mode

Wait a minute, I said before that using go keyword before a function call will execute that function in a new memory space, but the program is not working as expected. Why's that?

Well, the reason this program isn't working as expected is because when we create a new goroutine, that goroutine does the task it is assigned but while that task is being done the main goroutine AKA main function continues to do it's task and when that main goroutine completes it's task and program is terminated, the go runtime does not care whether child goroutines completes their task or not, it is the job of the developer to make the main goroutine wait for the child goroutines to complete their task and then terminating the program. so, how do we do that?.

We can see what happened in the above program by looking at the below diagram.

Image description

There are many ways to make main goroutine wait for child goroutine to complete their task and terminate the program. The most simple way is to make our program sleep for some time which will delay the termination of main goroutine while the child goroutines complete their task. Let's see that in action by modifying our previous example.

package main

import (
    "fmt"
    "time"
)

func sayHi(name string) {
    fmt.Printf("Hi %s\n", name)
}

func main() {
    fmt.Println("Program Started")

    go sayHi("Luffy")
    go sayHi("Zoro")
    go sayHi("Sanji")

    //sleeps the program for 3 seconds.
    time.Sleep(3 * time.Second)

    fmt.Println("Program Ended")
}
Enter fullscreen mode Exit fullscreen mode
Program Started
Hi Sanji
Hi Luffy
Hi Zoro
Program Ended
Enter fullscreen mode Exit fullscreen mode

Here in the above example we are using time.Sleep(3 * time.Second) to make the program sleep for 3 seconds, while the main goroutine sleeps for 3 seconds child goroutines now have the time to complete their task. After the main goroutine resumes after 3 seconds the program is then terminated. Using time.Sleep() is not the best way to make a blocking call and you shouldn't do this in a serious project you are build, instead you should use something knows as WaitGroup from go's sync standard library which we will be looking at some other time.

We can see what happened in this program by looking at the image below.

Image description

If you look at the output of the above program the order in which it greets us is random, this behavior occurs because a goroutine does not cares if other goroutine before it have completed their task or not, it's job is to do it's task and disappear. But in real world problems the order in which tasks are done matters a lot, so to achieve that there is something called channels which handles communication between goroutines, we will discuss about those in another article.

Conclusion

Goroutines are an essential part of the go programming language, goroutines helps to achieve concurrency in very simple way as explained above. But beware throwing go keyword here and there is a really good way to get yourself in a mess, it can make your code full of bugs and race conditions, so use them in very reasonable situations.

Happy Coding!

Top comments (0)