package main
import (
"fmt"
"time"
)
func main() {
limiter := make(chan bool, 10)
for i := 0; i < 100; i++ {
limiter <- true
go download(i, limiter)
}
}
func download(index int, limiter chan bool) {
time.Sleep(1 * time.Second)
fmt.Println("start to download :", index)
<-limiter
}
上面这段代码执行结果为什么最大打印值直到 89 ?
当把
time.Sleep(1 * time.Second)
fmt.Println("start to download :", index)
两行上下调换之后,变成以下的代码:
fmt.Println("start to download :", index)
time.Sleep(1 * time.Second)
就能正常打印到 99 了?
有疑问加站长微信联系(非本文作者)

问题的关键在于 main goroutine 提前退出了。
不论是那种代码,最好在 main 函数最后等待所有 goroutine 执行完成,方法很多了,可以 Sleep,可以 sync.WaitGroup 等等。
但是我只仅仅调换了下 sleep 和 print 语句的先后顺序,就能打印到 99 了。
按理说,调换 time 和 print的先后顺序是没法解决 main goroutinue 提前退出的问题的,但是为何程序却能正常完成99的打印?
说错了。。这两行代码的先后顺序无关,不论调换前后,结果都不能正常打印到99.
现在的问题是:在不使用 WaitGroup 和 在 main 主 goroutinue 强制 sleep 的方式外,怎样保证所有的 download goroutinue 都已经完成?意思是对于这个有缓冲的channel, 最终如何保证所有的goroutinue都已完成?
前提是 不能改变代码原有的逻辑:这段代码要实现的逻辑是控制并发数量,限制并发数量不能超过10。 所以在这种场景下,没法使用 sync.WaitGroup。
可以看看 http://books.studygolang.com/gopl-zh/ch8/ch8-06.html 关于控制 goroutine 并发数
供参考:
这个
curGNum
的没有自增操作么?如果要在goroutinue
里面对curGNum
自增操作的话,需要对这个进行加锁吧。想通了,通过
sync.WaitGroup
和channel
实现了控制并发数,代码如下:需要对 curGNum 自增!你这种方式,并没有真正控制 goroutine 数据。
sync.WaitGroup 负责管理产生的goroutinue,而 channel负责当并发数达到阈值的时候阻塞,从而避免继续产生goroutinue。 这样实现不对?
前面那段代码,并没有看到curGNum的自增操作啊
需要自增,我漏掉了而已
我这种 WaitGroup + channel 的实现方式有问题吗?我测试下来发现是可以的,既能控制并发数,又能正常完成所有的 goroutinue
你这样可以,刚才没看到
limiter <- true
有个小问题昂, 在所有worker goroutine们结束之后, 不是应该手动关闭limiter channel吗?
不好意思代码贴错了,这是我的版本
在 main goroutinue 之中单独开一条 goroutinue 执行 wait和 close 是出于什么考虑呢?
我在go语言圣经里看到这样一段代码,关于并发循环的,
http://books.studygolang.com/gopl-zh/ch8/ch8-05.html
@polaris 大神来解释下,为何要新开单独goroutinue来wait和close而不是直接wait和close?
如果等待操作被放在了main goroutine中,在循环之前,这样的话就永远都不会结束了,如果在循环之后,那么又变成了不可达的部分,因为没有任何东西去关闭这个channel,这个循环就永远都不会终止。
找到了,文中有说明,为何要这样写
Go 圣经中最后会 range sizes,所以有 close。 这里如果只是:
则 main goroutine 不是提前退出了吗?
这是无缓冲channel,如果不开goroutine来sync.wait(), makeThumbnails6函数会阻塞在wg.wait()这里等待所有goroutine结束,但是在goroutine中对sizes写数据的操作又会阻塞, 因为是无缓冲channel,而
这个读channel的操作永远得不到执行,因此会形成死锁
但是在你的程序里,在main中直接wg.Wait()即可,不需要另开一个goroutine,否则你的main会很快退出,你可能无法看到输出
上面格式不对, 编辑一下
这是无缓冲channel,如果不开goroutine来sync.wait(), makeThumbnails6函数会阻塞在wg.wait()这里等待所有goroutine结束,但是在goroutine中对sizes写数据的操作又会阻塞, 因为是无缓冲channel,而
这个读channel的操作永远得不到执行,因此会形成死锁
但是在你的程序里,在main中直接wg.Wait()即可,不需要另开一个goroutine,否则你的main会很快退出,你可能无法看到输出
嗯,确实如此,感觉不论后续是否有其他操作( 除
sleep
强制等待 和range channel
阻塞 之外),如果在 main 中新开goroutinue
去wg.wait
的话,都会有可能会导致main
比 负责wait
的goroutinue
提前结束。嗯嗯,不能这样写
请问这样写有不合适的吗?
wg.Add(20)表示要等待通道的个数~~~~
这样写会导致main可能提前退出,因为 每次download 的时候,limiter都在消费,所以最后的limiter未必是10个