- Hands-On Full Stack Development with Go
- Mina Andrawos
- 568字
- 2021-07-02 12:33:34
Goroutines
It's now time to dig deeper into the clean API that Go provides in order to write concurrent software with ease.
A goroutine is simply defined as a light-weight thread that you can use in your program; it's not a real thread. In Go, when you define a piece of code as a new goroutine, you basically tell the Go runtime that you would like this piece of code to run concurrently with other goroutines.
Every function in Go lives in some goroutine. For example, the main function that we discussed in the previous chapter, which is usually the entry point function for your program, runs on what is known as the main goroutine.
So, how do you create a new goroutine? You just append the go keyword before the function that you would like to run concurrently. The syntax is quite simple:
go somefunction()
Here, somefunction() is the piece of code that you would like to run concurrently. Whenever you create a new goroutine, it will get scheduled to run concurrently, and will not block your current goroutine.
Here is a trivial, but more complete piece of code to help us understand the concept of goroutines:
package main
import (
"fmt"
"time"
)
func runSomeLoop(n int) {
for i := 0; i < n; i++ {
fmt.Println("Printing:", i)
}
}
func main() {
go runSomeLoop(10)
//block the main goroutine for 2 seconds
time.Sleep(2 * time.Second)
fmt.Println("Hello, playground")
}
The preceding code is a simple program that runs a function called runSomeLoop() on a new goroutine. This means that runSomeLoop() will run concurrently to the main() function. In the program, we made use of a function called Sleep(), which exists in the time package. This function will block the main goroutine in order to give runSomeLoop() a chance to run and finish. If we don't block the main goroutine in this example, the main goroutine will likely finish, and then exit the program before runSomeLoop() gets a chance to fully run. There are cases when This is a byproduct of the fact that goroutines are concurrent, which is why invoking a new goroutine does not block your current goroutine.
Here's how the output of the program will look:
Printing: 0
Printing: 1
Printing: 2
Printing: 3
Printing: 4
Printing: 5
Printing: 6
Printing: 7
Printing: 8
Printing: 9
Hello, playground
This shows us that the runSomeLoop() goroutine managed to run at the same time when the main goroutine was sleeping. When the main goroutine woke up, it printed Hello, playground before it exited.
So, what if we removed the time.Sleep() function that blocked the main goroutine? Take a look at the following code block:
package main
import (
"fmt"
)
func runSomeLoop(n int) {
for i := 0; i < n; i++ {
fmt.Println("Printing:", i)
}
}
func main() {
go runSomeLoop(10)
fmt.Println("Hello, playground")
}
You will get the following result:
Hello, playground
You can see that runSomeLoop() didn't get the chance to run before the main goroutine exited.
Goroutines are very light from a memory and resources point of view; a production Go program will typically run hundreds and thousands of goroutines. The ability to produce goroutines with such simple API is arguably one of the most powerful features of the Go language, according to many of Go's users.
Let's take a look at Go channels in the next section.