Introduction to Goroutines

10 Apr 2021 ⏱️ 5 min
Introduction to Goroutines

Golang is a popular programming language. One of the major reasons people start using Go is due to its concurrency powers. In this article, we will learn the basics of concurrency and how Goroutines work.

Concurrency vs Parallelism

Before we do a deep dive into Goroutines, let’s first try to understand what is concurrency and how it is different from parallelism.

Concurrency is the execution of the multiple instruction sequences at the same time. It is composition of independently executing process. The execution order of tasks doesn’t effect the end result. There is a brilliant 30 min talk by Rob Pike where is explains how Concurrency is different from Parallelism. I highly recommend watching it to understand the core difference. Two key differences explained by Rob are -

  • Parallelism is simultaneous execution of multiple things at once. While Concurrency is composition of independently executing tasks.

  • Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.

Time for an example. In modern world, most applications and websites perform multiple independent tasks together. Let’s take Amazon and try to see what kind of concurrent processes it runs when we visit it.

  • Fetching recommended product for each user on home page
  • Getting best offers to lure you to buy products.
  • Fetching user reviews, product details and images for product you click.
  • Preparing a cart as you add items to it.
  • Processing payments, generating invoices, sending email about the purchase, etc.

So, there are hundreds of background tasks running concurrently to serve you. Thereby concurrency is super vital to build a good user experience. Do check my tutorial on running background tasks in Go.


What are Goroutines?

Goroutine are Golang’s solution to solving the concurrency problem. These are functions that run concurrently with other functions or methods. The concept is similar to that of threads in languages like Java but Goroutines are much more efficient and light-weight. We have a list of tasks, each of the individual tasks is assigned to a Goroutine which execute the tasks independenty.

  • Goroutines run in same address space.
  • They are much cheap to create comparing to threads - only 2kB of stack space.
  • Less switching costs. Goroutines are managed by a scheduler and they don’t directly interact with the OS kernel.

To use goroutines in golang - we have a go keyword. Let’s see a working example to learn more.

package main

import (
    "fmt"
)

func performSum(a int, b int) {
    sum := a + b
    fmt.Println("Sum of ", a, " and ", b, " is: ", sum)
}

func main() {
    go performSum(10, 20)
    fmt.Println("Back to main function")
}

In the above code, we have a simple performSum function which adds two numbers. In main we call this function with the go keyword i.e we call this function concurrently. Now, guess the output for the code above?

// Output
Back to main function

Surprised eh? Where is the print statement from performSum ? The reason we don’t see the print statement is because our program exited before performSum could evaluate the sum. The go control does not wait for the Goroutine to finish executing rather prints the main function and exits. So to actually get the result we need to prevent our main to exit. Let’s do that.

package main

import (
    "fmt"
    "time"
)

func performSum(a int, b int) {
    sum := a + b
    fmt.Println("Sum of ", a, " and ", b, " is: ", sum)
}

func main() {
    go performSum(10, 20)
    time.Sleep(1 * time.Second)
    fmt.Println("Back to main function")
}

Now, we added a Sleep() which results in main to sleep for 1 second giving goroutine enough time to execute and return. The output looks like -

// Output
Sum of  10  and  20  is:  30
Back to main function

But, you must be thinking isn’t this a wrong way. How would one know how much time to wait for each Goroutine to execute. Correct, here I used Sleep() method just to demonstrate, the right way is to use channels and wait groups. Channels can be thought of as pipes using which Goroutine communicate. I’ll cover details about channels in another article.

Understanding Concurrency in Go with Example

Understanding Concurrency in Go with Example

Let’s do one more slightly complex example. Here we have two functions tableOfFive() and tableOfSix() which print first 5 numbers in table of 5 and 6 respectively. We add a deliberate Sleep() in two functions so that we can see both these functions are running concurrently and independent of each other. Finally, we need to add a Sleep() in main to wait for above two functions to execute completely.

package main

import (
    "fmt"
    "time"
)

func tableOfFive() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i * 5)
        time.Sleep(250 * time.Millisecond)
    }
}

func tableOfSix() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i * 6)
        time.Sleep(500 * time.Millisecond)
    }
}

func main() {
    go tableOfFive()
    go tableOfSix()
    time.Sleep(5 * time.Second)
    fmt.Println("Back to main")
}

The output for the above code might be different for you but you will be mix of numbers from table of 5 and 6. Here is what I got as output.

5
6
10
15
12
20
25
18
24
30
Back to main

Resources

Books to learn Golang

More articles on Golang you might find interesting.


I hope you learned something new. Feel free to suggest improvements ✔️

I share regular updates and resources on Twitter. Let’s connect!

Keep exploring 🔎 Keep learning 🚀

Liked the content? Do support :)

Paypal - Mohit Khare
Buy me a coffee