What Makes Concurrency in Go so Special (golang concurrency patterns)

Michael Levan - Nov 2 '20 - - Dev Community

One of the many things that bring developers to Go is it's concurrency model. Because Go was released in 2009 after most popular programming languages like Python and Java, it wasn't written with a single threaded environment in mind.

It was built with concurrency as a first-class citizen.

Let's take a look at what concurrency is and how it can be used in Go.

Concurrency

Think about your morning routine for a second (I'm just guessing here). You wake up, take a shower, get dressed, make some coffee, eat breakfast, and check your Slack messages. These are all things that you're doing, but you aren't doing them at the same time. In fact, it might be a little difficult to make coffee and take a shower at the same time.

Because you aren't doing them at the same exact time, you're doing them independently.

The same rules apply for programming.

Let's say you have two functions and you need them both to run, but not at the same time. Perhaps there's a dependency for a function to run before another. For example, credentials need to be created from one function and passed into another function. There's also the efficiency aspect. You want to be able to make progress on more than one task simultaneously.

Concurrency is about executing independently.

Concurrency vs Parallelism

Concurrency and parallelism are not the same. That fact is something that's brought up quite a lot when you're new to concurrency.

Let's take the morning route that you read in the previous section. Remember, concurrency is about doing all of those tasks simultaneously. Parallelism is about doing all of those tasks at the same time.

For example, let's say you have two functions. One of those functions makes an API request and the other adds 2 + 2. Completely different functions, but they run at the same time.

What Makes Go Special

Let's get to the good stuff. What makes concurrency one of the biggest talking points when it comes to Go?

Go was built with concurrency in mind. It was built after a lot of programming languages came out that were focused on single-core threads. That's because when they were created, single-core threads was all that existed.

As you all know, as time goes on, applications get more powerful. Hardware gets more powerful. Think about it, I bet (no money because who knows) you can't find a single-core laptop anymore. Even if you did, it would be incredibly slow.

So, here's the thing. Other programming languages have concurrency. What makes Go's so popular? Goroutines.

Goroutines are ridiculously small and lightweight. They cost near to nothing when it comes to threads and memory. They run independently of the function that started it.

Concurrency does use threads, but Goroutines are layered on top of threads. It's an incredible lightweight feature.

Why Use Goroutines?

Because it's lightweight. That's pretty much what it comes down to. When you think about a traditional OS thread, it's typically one megabyte. When using Goroutines, it's kilobytes.

Goroutines are far smaller than threads.

Another large piece of the pie is when it comes to OS threads, the OS manages the scheduling. With goroutines, the Go runtime manages the scheduling. Everything from creating them to ripping them apart.

Let's break it down into bullet points:

  • Lightweight
  • Less OS thread switching
  • The Go runtime manages concurrency (goroutines)

Writing a Concurrent Program in Go

Now that you know why concurrency is pretty popular in Go, let's take a look at how to create a Go application using concurrency.

This code can be found on my GitHub here.

First, you'll set up the main package and the imports. Create a new file called main.go and add in the following code.



package main

import (
    "fmt"
    "time"
)


Enter fullscreen mode Exit fullscreen mode

Next, you'll add function number one. The first function is number(), which prints out a number of your choosing.



func number(two int) {

    num := two
    fmt.Println(num)
}


Enter fullscreen mode Exit fullscreen mode

The second function is called sayGo(), which prints out I love Go.



func sayGo() {
    love := "I love Go"
    fmt.Println(love)
}


Enter fullscreen mode Exit fullscreen mode

After the two functions are written, it's time to write the main() function. The main function will consist of three things:

  • Calling the sayGo() function as a goroutine
  • Putting the program to sleep for 2 seconds for the concurrency to run
  • Calling the number() function

So, how do you define a goroutine? Simple. You literally just put the go keyword in front of the function that you want to run as a goroutine.



func main() {
    go sayGo()

    time.Sleep(2 * time.Second)

    number(2)
}


Enter fullscreen mode Exit fullscreen mode

All together, the code should look like the below.



package main

import (
    "fmt"
    "time"
)

func main() {
    go sayGo()

    time.Sleep(2 * time.Second)

    number(2)
}

func number(two int) {
    num := two
    fmt.Println(num)
}

func sayGo() {
    love := "I love Go"
    fmt.Println(love)
}


Enter fullscreen mode Exit fullscreen mode

Now it's time to run the program. On the terminal, run the following command:

go run main.go

You will see a the concurrent program run, then a sleep of two seconds, followed by the number 2 printed out to the console.

https://www.clouddev.engineering/content/images/2020/11/2020-11-01_19-08-30.png

Congrats! You have officially created your first goroutine.

Summary

This was a fairly small example, but as you can see, it's quite simple to create a goroutine and they can make your program perform better from a benchmark perspective and a hardware perspective, depending on where the program is running.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .