Issue
I’m experimenting with the concurrency in golang. I had written a code that calls URL and extract content type from Header. I had approached in two ways. Approach 1 works and Approach 2 is not producing expected result. I want know a way to make my approach 2 work.
Approach 1
Here I sent string to channel ch
from within ctypes
.
package main
import (
"fmt"
"net/http"
)
func ctypes(url string, ch chan string) {
resq, err := http.Get(url)
if err != nil {
ch <- err.Error()
return
}
defer resq.Body.Close()
ct := resq.Header.Get("Content-type")
if ct == "" {
ch <- fmt.Sprintf("No such field in %v", url)
return
}
ch <- fmt.Sprintf("URL: %v; Content Type: %v", url, ct)
return
}
func main() {
var sites []string
ch := make(chan string)
sites = []string{
"https://golang.org",
"https://api.github.com",
"https://youtube.com",
}
for _, url := range sites {
go ctypes(url, ch)
}
for out := range sites {
fmt.Println(out)
}
}
OUTPUT 1: Producing desired results.
go run sites_with_channel.go
URL: https://api.github.com; Content Type: application/json; charset=utf-8
URL: https://golang.org; Content Type: text/html; charset=utf-8
URL: https://youtube.com; Content Type: text/html; charset=utf-8
Approach 2
Created anonymous function in a for loop inside a main
to send string into a channel
package main
import (
"fmt"
"net/http"
)
func ctypes(url string) string {
resq, err := http.Get(url)
if err != nil {
return err.Error()
}
defer resq.Body.Close()
ct := resq.Header.Get("Content-type")
if ct == "" {
return fmt.Errorf("No such field in %v", url).Error()
}
return fmt.Sprintf("URL: %v; Content Type: %v\n", url, ct)
}
func main() {
var sites []string
ch := make(chan string)
sites = []string{
"https://golang.org",
"https://api.github.com",
"https://youtube.com",
}
for _, url := range sites {
go func() {
ch <- ctypes(url)
}()
}
for range sites {
out := <-ch
fmt.Println(out)
}
defer close(ch)
}
Output 2
go run cont.go
URL: https://youtube.com; Content Type: text/html; charset=utf-8
URL: https://youtube.com; Content Type: text/html; charset=utf-8
URL: https://youtube.com; Content Type: text/html; charset=utf-8
Solution
The loop variable is rewritten in for loops, so when the goroutine reads url
, it is probably rewritten already. This is a common error. You should use a copy of the loop-variable in the goroutine.
for _, url := range sites {
go func(u string) {
ch <- ctypes(u)
}(url)
}
Answered By – Burak Serdar
Answer Checked By – Katrina (GoLangFix Volunteer)