Learn Go: Introduction to Concurrency With Golang Goroutines #28

In this tutorial, we are going to be looking at how you can use goroutines within you Go programs

Gopher

Lets go!!

Published · Feb 9


In the previous tutorial, we discussed about concurrency and how it is different from parallelism. In this tutorial, we will discuss about how concurrency is achieved in Go using Goroutines.


. . .

What are Goroutines?

Goroutines are functions or methods that run concurrently with other functions or methods. Goroutines can be thought of as light weight threads. The cost of creating a Goroutine is tiny when compared to a thread. Hence its common for Go applications to have thousands of Goroutines running concurrently.

Advantages of Goroutines over threads
You can think of goroutines as threads but with a few key differences :
  • Goroutines are lighter with an average size of 8kB
  • Goroutines communicate with channels whereas thread communication is more complex
  • Goroutine scheduling is managed by the go at runtime
  • Goroutines do not have any identity
  • Goroutines are run on real OS threads, the number of which is controlled by GOMAXPROCS
. . .

Goroutine states

For this tutorial, it is helpful to think of goroutines as always being in one of the following states:

  • Waiting: the goroutine is not blocked by a blocking statement but is not yet being executed by the scheduler.
  • Running: The goroutine is being executed by the scheduler
  • Blocking: The goroutine is being blocked by a blocking operation e.g channel, sleep, network operation. The scheduler moves on to the next available goroutine
  • Executed: The goroutine has been fully executed and exited. E.G main() function closing
. . .

How to start a Goroutine?

A goroutine is denoted simply by adding the word ‘go’ in front of the function call. For example, for some function f:
func f(a string, b string) { fmt.Printf("%s %s", a, b) }

We can call this function in a new goroutine like this:
go f("foo", "bar")
Looking at this code:
package main

import (
"fmt"
)

func f(a string, b string) {
fmt.Printf("%s %s", a, b)
}

func main() {
go f("foo", "bar")
}
The side() function is being called to run as a goroutine but we do not receive output from it yet, why? The goroutine() is still on ready and the scheduler does not run it throughout the app's runtime.

If we make a change like this:
package main

import (
"fmt"
"time"
)

func f(a string, b string) {
fmt.Printf("%s %s", a, b)
}

func main() {
go f("foo", "bar")

time.Sleep(time.Second * 1)
}


The call to time.Sleep (a blocking statement) in the main goroutine put's that goroutine in a blocked state. That allows the go scheduler run any other scheduled goroutines that are ready to be run, in this case the goroutine() is executed.

From this, you see that go does not run a goroutine until it is scheduled to. And how do you get a goroutine to run? By using a getting all other goroutines lined up before it to run or blocking it.
. . .


How to start Multiple Goroutine?

Considering this code:
package main

import (
"fmt"
"time"
)

func main() {
go sayWord("goroutine1")
go sayWord("goroutine2")
sayWord("Main")
time.Sleep(time.Millisecond)
fmt.Println("End of main")
}

func sayWord(word string) {
for i := 0; i < 2; i++ {
fmt.Println(word)
}
}


below is the output of the program
Main Main goroutine1 goroutine1 goroutine2 goroutine2 End of main

This is what's going on:


The call to time.Sleep (a blocking statement) in the main goroutine put's that goroutine in a blocked state.

That allows the go scheduler run any other scheduled goroutines that are ready to be run, in this case the sayWord() is executed.
. . .

Real world Use Case

It will be evident in the following code:
package main

import (
"fmt"
"net/http"
"sync"
)

var urls = []string{
"https://google.com",
"https://facebook.com",
"https://twitter.com",
}

func checkLink(url string, wg *sync.WaitGroup) (string, error) {
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
return "", err
}
wg.Done()
fmt.Println(resp.Status)
return resp.Status, nil
}

func main() {

fmt.Println("HomePage Endpoint Hit")
var wg sync.WaitGroup

for _, url := range urls {
wg.Add(1)
go checkLink(url, &wg)
}

wg.Wait()
}


Each checkLink function executes in a new goroutine (path of execution) and the main goroutine is blocked
. . .

Conclusion

So, in this tutorial, we learned how we can get started developing concurrent applications in Go.

We looked at what Goroutines are and how we can use them to speed up various parts of our systems and create performant applications.

Hopefully, you found this tutorial useful, if you did then please let me know in the comments section below!
. . .

. . .

Never miss a post from Gopher , when you sign up for Ednsquare.