Getting started with goroutines and channels as beginners
Getting started with goroutines and channels as beginners
Go routines are essentially like thread. If I can say it in a simple way, it is a concurrently running task. We have one main
go routine that runs by default and executes all the tasks, like the exercises that we have been doing so far
To create a new goroutine we can simply do
go functionName()
// or we can create an anyonymours function inline
go func(){
// do something
}()
// the () invokes the function immediately after the declaration
So in a normal scenario, lets say we do something like
package main
import (
"fmt"
)
type Resp struct {
idx int
}
func functionName(idx int) (Resp, error){
fmt.Println("API CALL for idx = ", idx)
return Resp{idx: idx}, nil ;
}
func main() {
data, _ := functionName(0)
fmt.Print(data)
}
// the output is
/**
API CALL for idx = 0
{0}
*/
Now to make it concurrent, simply doing, wont work. Why? Lets see
package main
import (
"fmt"
)
type Resp struct {
idx int
}
func functionName(idx int) (Resp, error){
fmt.Println("API CALL for idx = ", idx)
return Resp{idx: idx}, nil ;
}
func main() {
var data Resp
go functionName(1)
fmt.Print(data)
}
// output is
// {0}
Few points to note here are
- Cannot directly assign the goroutine to a variable like this
data := go functionName()
it results in error - If you look at the output it shows the default values, whereas we passed idx as 1 and also the message “API call for idx” was not printed
This happens because as soon as we start new goroutine, the function call moves to another thread and the main goroutine continues with the next task. In this case it printed data and exited. As per the note, I mentioned earlier, once the main goroutine ends, all other executing goroutines are terminated as well.
Now we need the resp from the function call, just as before, right? This is where Channnels come into the picture
Channels
Channel as the name suggests, it is a path for communication between goroutines. Go has a specific type chan
for creating a channel. Read more about it in official doc. A channel provides two way communication between the goroutines ie it can be used for both sending and receiving the data
To create a new channel
// create a channel for int data values
ch := make(chan int)
For the above usecase we rewrite or modify the code slightly
package main
import (
"fmt"
)
// create a type that will be passed between go routines
type Api struct {
resp Resp
err error
}
// define a type that will be the API response type
type Resp struct {
idx int
}
func functionName(idx int, ch chan Api) (Resp, error){
fmt.Println("API CALL for idx = ", idx)
ch <- Api{ resp: Resp{idx: idx}, err: nil }
return Resp{idx: idx}, nil
}
func main() {
var data Api
ch := make(chan Api) // create a channel
go functionName(1, ch) // pass the channel as argument
data = <-ch // receive the data from channel once it sent from their
fmt.Print(data.resp, data.err)
}
// output
// API CALL for idx = 1
// {1} <nil>
Few points to note
- The line
data = <-ch
is a blocking operation, ie it will wait until a response is received from the channel. So lets say the API response is returned after 5 secs, this line will keep on waiting. - Closing the channel. As a beginner in Go and from my reading at this point in time, you should close channels from the sender side and not from the receiver side. The
chan
type has a methodclose(channelName)
to close the channel. In our scenario close will be called once we have sent the data infunctionName()
and will not be sending anymore
Note: All the goroutines are terminated automatically once the main goroutine ends.Irrespective of whether the goroutine is completed or not, it will be terminated