Goroutines: Understanding Concurrency in Go
Tags: beginners, go, programming, webdev
Published: 2023-02-07 09:34:27

One of the reasons Go is popular is its fascinating support for concurrency. Concurrency is the ability of a program to perform multiple operations at once. This is essential because we are now in a world where speed is a priority.

Go is packed with features like Goroutines, Channels, and Select. These features enable developers to build faster and more highly scalable applications.

## What is a Goroutine?

A Goroutine is an entity that can be executed concurrently and independent of other functions.
Whenever you start your Go program, a process is created on your computer. This process is just an environment that contains instructions and all the resources needed to run your program. A process creates a thread that is smaller and lighter. Goroutines are extremely lightweight and they live in the thread of an operating system.


![Goroutines in threads](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhibyead6sz3cxhp1422.jpg)

Creating a Goroutine does not require a lot of memory. Each Goroutine consumes only 2kB of stack space. Thus, running hundreds or thousands of them is not a problem. Since Goroutines are lighter than threads and threads are lighter than processes, it gives us the ultimate speed.

## Creating a Goroutine
Creating a Goroutine is as easy as adding a special keyword called `go` before your function call. Almost any function can be called with the keyword.

Below is an example of what a Goroutine looks like:

```
package main

import (
    "fmt"
    "time"
)

func helloWorld() {
    for i := 0; i < 10; i++ {
        fmt.Print(i)
    }
    fmt.Println("Hello world")
}

func goodByeWorld() {
    fmt.Println("Goodbye world")
}

func main() {
    go helloWorld()
    go goodByeWorld()
    fmt.Println("main")
    time.Sleep(1 * time.Second)
}
```

The program above contains two functions: `helloWorld()` and `goodByeWorld()`. A `for` loop was added in `helloWorld()` just to simulate the effect of Goroutines. Adding a `time.Sleep` of 1 second ensures that all our function runs before the program exits.
What the `go` keyword does in the program above is just tell the program to move on to the next task while the `helloWolrd()` and `goodByeWolrd()` function runs in the background.

Here is what the output looks like on the first run:

```
main
Goodbye world
0123456789Hello world
```
Here is the output on the second run:

```
main
0123456789Hello world
Goodbye world
```
As seen in the outputs above, the results are not in the order they were created in the code and they are not deterministic. Why? Because of the introduction of the `go` keyword. This means that the order in which Goroutines will be executed cannot be controlled without taking extra care. Extra care means writing extra code.

## Waiting for a Goroutine to finish

In the section above, there is a part of the code that looks like this:

```
func main() {
    go helloWorld()
    go goodByeWorld()
    fmt.Println("main")
    time.Sleep(1 * time.Second)
}
```
The `time.Sleep` function delays the program to ensure all Goroutine runs before the program exits. This is not ideal at the production level because running millions of concurrent requests can cause problems and the time it will take all goroutines to complete can’t exactly be predicted.

Solving this requires the use of `sync.WaitGroup` like in the code below:

```
package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func helloWorld() {
    defer wg.Done()
    for i := 0; i < 10; i++ {
        fmt.Print(i)
    }
    fmt.Println("Hello world")
}

func goodByeWorld() {
    defer wg.Done()
    fmt.Println("Goodbye world")
}

func main() {
    wg.Add(2)
    go helloWorld()
    go goodByeWorld()
    fmt.Println("main")
    wg.Wait()
}

```

In the code above, a variable `wg` of type `sync.WaitGroup` was declared.
`wg.Add(2)` increases WaitGroup count by 2, which tells the program to wait for 2 goroutines: `helloWorld()` and `goodByeWorld()`.
`wg.Done()` reduces the count by 1, meaning that one of the functions is done running. A `defer` keyword is added to ensure the `helloWorld()` and `goodByeWorld()` function is only marked as done after completion.
`wg.Wait()` is a blocking command to make the program wait until all goroutines are completed i.e the counter is 0.

Here is what the output looks like:

```
main
Goodbye world
0123456789Hello world

```
The output is the same as before, but this time we are not blocking the program with `time.Sleep` for 1 second. The `sync.WaitGroup` does all the magic.

## Conclusion

This article covered the basics of concurrency in Go. There are more concepts such as Channels, Mutexes and Pipelines. Hopefully, I will be shedding more light on these concepts in my future writings.