如何在并发操作中关闭通道?
How to close the channel in a concurrent operation?
我写了一个关于并发和通道的go代码⤵️
package main
import (
"fmt"
"net/http"
)
var links = []string{
"https://mcevik.com",
"https://whosebug.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
输出就是这样⤵️
在输出中我们看到程序没有终止。程序没有终止的原因是通道没有关闭,无法退出循环
如何关闭频道并修复代码?
如果你恰好启动N(即len(links)
)个Go例程,所有这些例程都必然会发回一条消息,那么最简单的事情就是在关闭通道之前从通道中读取恰好N条消息。
不要range
越过频道;当您不知道会收到多少项目并且想在频道关闭之前阅读时,这是最有用的。而是循环给定的次数:
// main:
for _ = range links {
fmt.Println(<-ch)
}
close(ch)
使用 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)
}
通过将 WaitGroup 添加到 getLink
方法对其进行重构,
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://whosebug.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)
}
}
我写了一个关于并发和通道的go代码⤵️
package main
import (
"fmt"
"net/http"
)
var links = []string{
"https://mcevik.com",
"https://whosebug.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
输出就是这样⤵️
在输出中我们看到程序没有终止。程序没有终止的原因是通道没有关闭,无法退出循环
如何关闭频道并修复代码?
如果你恰好启动N(即len(links)
)个Go例程,所有这些例程都必然会发回一条消息,那么最简单的事情就是在关闭通道之前从通道中读取恰好N条消息。
不要range
越过频道;当您不知道会收到多少项目并且想在频道关闭之前阅读时,这是最有用的。而是循环给定的次数:
// main:
for _ = range links {
fmt.Println(<-ch)
}
close(ch)
使用 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)
}
通过将 WaitGroup 添加到 getLink
方法对其进行重构,
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://whosebug.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)
}
}