goroutine channel的坑
channel的关闭
for-range
知道channel的关闭,代码继续执行for-select
不知道,用case v, ok:=<-c:
中的ok来判断- 那么问题来了,如果是一个buffer channel,
ok
在关闭的时候可不是true,而是满足channel数据去完
&channel close
,ok
才会返回true,如果不这样设计的话,会导致数据丢失。
- 那么问题来了,如果是一个buffer channel,
channel for-select 中break和return的问题
核心是,break
在select中是只能跳出select
,return
是返回当前的函数。
package meituri
import "fmt"
type ISuiteOperator interface {
GetPageURLs(chan string)
GetImgURLs(chPage <-chan string, chFailedImg <-chan string) <-chan string
Download(chImg <-chan string, chFailedImg chan string, folderPath string) <-chan string
}
// DonwloadSuite to download one suite
func DonwloadSuite(iSuite ISuiteOperator, countFanOut int, folderPath string) {
chPage := make(chan string)
chFailedImg := make(chan string) // 下载失败img放回
go iSuite.GetPageURLs(chPage)
var chImgs []<-chan string
for i := 0; i < countFanOut; i++ {
ch := iSuite.GetImgURLs(chPage, chFailedImg)
chImgs = append(chImgs, ch)
}
// 回收多个channel的结果
chImg := merge(chImgs...)
// for debug
// for {
// select {
// case v, ok := <-chImg:
// if !ok {
// return
// }
// fmt.Println(v, ok)
// }
// }
var chDownloads []<-chan string
for i := 0; i < countFanOut; i++ {
ch := iSuite.Download(chImg, chFailedImg, folderPath)
chDownloads = append(chDownloads, ch)
}
finish := merge(chDownloads...)
for ret := range finish {
fmt.Println("finish: ", ret)
}
}
在上面的代码中,chImg在merge结果的时候,注释代码是去测试结果的。
- 如果在chImg关闭的时候
return
,此时应该是退出DonwloadSuite
这个方法,注意此时使用break
只是breakselect
而已,还是在for循环中,这是golang for-select的大坑 - 比如在下面的代码中,在
go func(){}
中使用return
是退出当前的goroutine,可以触发goroutine中注册的defer
来关闭已经写入完毕的channel
以达到让使用者感知通信结束,使用for-range可以直接感知,for-select需要用case val, ok:= <-c:
中的ok的bool值判断channel是否关闭
func (suite MeituriSuite) GetImgURLs(chPage <-chan string, chFailedImg <-chan string) <-chan string {
out := make(chan string)
go func() {
defer close(out)
for {
select {
case url, ok := <-chPage:
if !ok {
fmt.Println("no more url")
return
}
content := getPageContent(url)
divContent := parseDivContent(content)
imgSrcs := parseImg(divContent)
// fmt.Println(imgSrcs)
for _, imgSrc := range imgSrcs {
out <- imgSrc
}
case url := <-chFailedImg:
out <- url
}
}
}()
return out
}