Reputation: 33
I wrote a go code like that about concurrency and channels ⤵️
package main
import (
"fmt"
"net/http"
)
var links = []string{
"https://mcevik.com",
"https://stackoverflow.com",
"https://www.linkedin.com",
"https://github.com",
"https://medium.com",
"https://kaggle.com",
}
func getLink(link string, ch chan string) {
if res, err := http.Get(link); err != nil {
ch <- err.Error()
} else {
ch <- fmt.Sprintf("[%d] - %s", res.StatusCode, link)
}
}
func main() {
ch := make(chan string, len(links))
for _, link := range links {
go getLink(link, ch)
}
for msg := range ch {
fmt.Println(msg)
}
}
https://play.golang.org/p/Uz_k8KI6bKt
and the output is like that ⤵️
In the output we see that the program is not terminated. The reason for the program's not terminated is that the channel has not closed and therefore cannot exit the loop.
How can I close the channel and fix the code?
Upvotes: 0
Views: 431
Reputation: 33
Refactored it by adding WaitGroup to getLink
method,
func getLink(link string, wg *sync.WaitGroup, ch chan string)
and channel closed after wg.Wait()
call.
go func() {
wg.Wait()
close(ch)
}()
As a result, the final version of the code looks like this ⤵️
package main
import (
"fmt"
"net/http"
"sync"
)
var links = []string{
"https://mcevik.com",
"https://stackoverflow.com",
"https://www.linkedin.com",
"https://github.com",
"https://medium.com",
"https://kaggle.com",
}
func getLink(link string, wg *sync.WaitGroup, ch chan string) {
defer wg.Done()
if res, err := http.Get(link); err != nil {
ch <- err.Error()
} else {
ch <- fmt.Sprintf("[%d] - %s", res.StatusCode, link)
}
}
func main() {
wg := sync.WaitGroup{}
ch := make(chan string, len(links))
for _, link := range links {
wg.Add(1)
go getLink(link, &wg, ch)
}
go func() {
wg.Wait()
close(ch)
}()
for msg := range ch {
fmt.Println(msg)
}
}
https://play.golang.org/p/741F8eHrhFP
Upvotes: 0
Reputation:
use a WaitGroup to watch for writes completion.
ch := make(chan string, len(links))
var wg sync.WaitGroup
for _, link := range links {
wg.Add(1)
go func(){
getLink(link, ch)
wg.Done()
}()
}
Use another routine to listen that event and close the channel.
go func(){
wg.Wait()
close(ch)
}()
for msg := range ch {
fmt.Println(msg)
}
Upvotes: 1
Reputation: 239270
If you start exactly N (ie len(links)
) Go routines, all of which will necessarily send back a message, then the simplest thing is to read exactly N messages from the channel before closing it.
Don't range
over the channel; that's most useful when you don't know how many items you'll receive and you want to read until the channel is closed. Instead loop a given number of times:
// main:
for _ = range links {
fmt.Println(<-ch)
}
close(ch)
Upvotes: 0