英文:
How to close the channel in a concurrent operation?
问题
我写了一个关于并发和通道的 Go 代码,如下所示:
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)
}
}
输出结果如下所示:
从输出结果中我们可以看到程序没有终止。程序没有终止的原因是通道没有关闭,因此无法退出循环。
如何关闭通道并修复代码呢?
英文:
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?
答案1
得分: 1
使用WaitGroup来监视写入操作的完成。
ch := make(chan string, len(links))
var wg sync.WaitGroup
for _, link := range links {
wg.Add(1)
go func(){
getLink(link, ch)
wg.Done()
}()
}
使用另一个协程来监听该事件并关闭通道。
go func(){
wg.Wait()
close(ch)
}()
for msg := range ch {
fmt.Println(msg)
}
英文:
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)
}
答案2
得分: 0
如果你启动了确切数量为N(即len(links)
)的Go协程,而且所有协程都必须发送一条消息回来,那么最简单的方法是在关闭通道之前从通道中读取确切数量为N的消息。
不要使用range
遍历通道;这在你不知道会接收到多少项并且想要一直读取直到通道关闭时最有用。相反,循环指定的次数:
// main:
for _ = range links {
fmt.Println(<-ch)
}
close(ch)
英文:
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)
答案3
得分: 0
通过在getLink
方法中添加WaitGroup,对代码进行了重构,
func getLink(link string, wg *sync.WaitGroup, ch chan string)
并在wg.Wait()
调用后关闭了通道。
go func() {
wg.Wait()
close(ch)
}()
结果,代码的最终版本如下所示 ⤵️
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
英文:
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)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论