Table of Contents
- Introduction
- What are Goroutines?
- How to start a Goroutine
- How to check the number of all existing goroutines
- Channels
- How to declare a channel
- Sending and receiving from a channel
- Sends and receives are blocking by default
- More Resources
Introduction
This post is about goroutines
and channels
. This is the last post of My Journey of Go
and might have been the hardest one to write about since I was not familiar with the concepts at all.
What are Goroutines?
A goroutine
is a lightweight thread managed by the Go runtime.
How to start a Goroutine
You can declare a goroutine
by adding the keyword go
before a function or method invocation like the code below.
go functionName(arg, ...)
In the following code, go hello()
starts a new goroutine
and hello()
will run concurrently along with the main
function which runs in its own Goroutine and its called the main Goroutine
package main
import "fmt"
func hello() {
fmt.Println("Hello World")
}
func main() {
go hello()
fmt.Println("main function")
//=> main function
}
The code above only outputs main function
. This is due to the following reasons.
Firstly, when a new goroutine
starts running, the goroutine call returns immediately. It does not wait for the goroutine to finish executing.
Secondly, the main Goroutine
should be running for any other goroutines
to run. If the main goroutine
terminates, the program will be terminated and no other goroutine
will run.
Now you understand why it didn’t output Hello World
. In the code, after calling go hello()
, the control returned immediately to the next line of code which is fmt.Println("main function")
without waiting for the hello goroutine
to finish.
You can execute hello()
by giving it enough time to run before the main goroutine
terminates.
package main
import (
"fmt"
"time"
)
func hello(){
fmt.Println("Hello World")
}
func main(){
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
//=> Hello World
//=> main function
}
Normaly, channels are used to block the main goroutine
until all other goroutines
finish their execution.
How to check the number of all existing goroutines
You can use runtimeNumGoroutine()
to get all the existing goroutines in your code.
package main
import (
"log"
"runtime"
"fmt"
)
func hello(){
fmt.Println("Hello World")
}
func main(){
go hello()
fmt.Println("main function")
log.Println(runtime.NumGoroutine()) //-> 2018/10/09 07:39:07 2
}
Channels
Channels
provide a way for goroutines
to communicate with one another and synchronize their execution. Data can be sent from one goroutine and received from another goroutine.
How to declare a channel
You can declare a channel
like the code below.
ch := make(chan TYPE)
Each channel
has a type associated with it. This type is the type of data that the channel is allowed to transport. You can not transport any other data using the channel
.
Sending and receiving from a channel
You can send and receive data from a channel
using the syntax below.
ch <- data // write to channel ch
variable := <- cd // read from channel ch
In the first line, the arrow points towards ch
and this means we are writing data to channel ch
.
In the second line, the arrow points outwards from ch
and this means we are reading from ch
and storing data to variable
.
package main
import "fmt"
func main() {
//Create a new channel
ch := make(chan string)
//Write data to ch
go func() { ch <- "data" }()
//Reading data from ch
variable := <-ch
fmt.Println(variable) //=> data
}
Sends and receives are blocking by default
By default, sends and receives block until the other side is ready. That means when a data is sent to a channel, the control is blocked until some other goroutine
reads from that channel.
This allows goroutines
to synchronize without explicit locks or condition variables.
Example1
In the following code, I created a boolean type channel named ch
and declare a function as a goroutine. Then, I passed a boolean value to ch
in the function.
The <-ch
statement will block the code until some boolean data is received on the ch
channel.
package main
import "fmt"
func main() {
ch := make(chan bool) //Create a boolean type channel
//Run the following function as a goroutine.
go func() {
fmt.Println("Hello")
ch <- true //Pass a boolean value to the channel
}()
<- ch //Block until ch receives a boolean value
}
Example2
In this example, I defined a function called hello
which accepts a channel
as its argument. And then, I created a channel called ch
in the main goroutine
and passed it as a parameter to the hello()
goroutine.
Like [Example1](# example1), the <-ch
statement will block the code until some boolean data is received on the ch
channel.
package main
import "fmt"
func hello(channel chan bool) {
fmt.Println("Hello world")
channel <- true
}
func main() {
ch := make(chan bool) //Create a boolean type channel
go hello(ch) //Run hello method as a goroutine
<-ch //Block until ch receives a boolean value
fmt.Println("main function")
//=> Hello world
// main function
}
More Resources
GOLANGBOT.COM Go Resources (Concurrency) A Tour of Go (Goroutines)