š©š»āš»Study Log_Go_Goroutine_Channel

GoRoutine & Channel
A goroutine is a lightweight thread managed by the Go runtime. You can just put āgoā in front of the function you want to run concurrently. Also, you can create channel that receives from goroutine to see if it finishes without any issues or to communicate cross goroutines.
Then, letās compare how fast a for loop is versus using a goroutine.
Letās say we have a function to get a response for each website with http.Get function. (URLChecker)
If we use typical for loop to call the function and printing out the result after putting them to a map, it took 3.116982042s.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
start := time.Now()
results := make(map[string]string)
urls := []string{
"https://www.airbnb.com/",
"https://www.google.com/",
"https://www.amazon.com/",
"https://www.reddit.com/",
"https://www.facebook.com/",
"https://www.instagram.com/",
}
for _, url := range urls {
result := hitURLFor(url)
results[url] = result
}
for url, status := range results {
fmt.Println(url, status)
}
elapsed := time.Since(start)
fmt.Printf("Execution time: %s\n", elapsed)
}
func hitURLFor(url string) string {
resp, err := http.Get(url)
status := "OK"
if err != nil || resp.StatusCode >= 400 {
status = "FAILED"
}
return status
}
Then, Letās try goroutine. The code is below and it took only 493.601917ms. š«¢
package main
import (
"fmt"
"net/http"
"time"
)
type requestResult struct {
url string
status string // ok or failed
}
func main() {
start := time.Now()
results := make(map[string]string)
c := make(chan requestResult)
urls := []string{
"https://www.airbnb.com/",
"https://www.google.com/",
"https://www.amazon.com/",
"https://www.reddit.com/",
"https://www.facebook.com/",
"https://www.instagram.com/",
}
for _, url := range urls {
go hitURL(url, c)
}
for i := 0; i < len(urls); i++ {
//fmt.Println(<-c) // printing messages from channels
result := <-c
results[result.url] = result.status
}
for url, status := range results {
fmt.Println(url, status)
}
elapsed := time.Since(start)
fmt.Printf("Execution time: %s\n", elapsed)
}
func hitURL(url string, c chan<- requestResult) {
// <- this channel will be send only
//fmt.Println("Checking:", url)
resp, err := http.Get(url)
status := "OK"
if err != nil || resp.StatusCode >= 400 {
status = "FAILED"
}
c <- requestResult{url: url, status: status}
}
How could this be so fast and whatās going on behind the scene?
As mentioned earlier, goroutines allow jobs to run concurrently, unlike a for-loop, enabling tasks complete much faster. However, if the main function returns before the goroutines finish, they will be terminated. Therefore, we need channels to notify the main function that goroutines are still running.
We can create a channel as shown below and use it to return a struct-type value, requestResult
(which contains a URL and status). You may notice some arrows (ā
, ā
); these indicate the direction of communicationāwhether the channel is being used for sending or receiving data.
For example:
c chanā requestResult
means "send arequestResult
value to channelc
."result := āc
means "receive a value from channelc
and assign it to the variableresult
."
Also, if you look at the argument of the hitURL
function, it has c chanā requestResult
. We can also write this as c chan requestResult
without the ā
because chan requestResult
already implies that it's a channel for that type. However, adding ā
explicitly indicates that the channel is send-only.
c := make(chan requestResult)
More details : https://go.dev/ref/mem#chan
Subscribe to my newsletter
Read articles from HAYEON SON directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
