Go 系列教程 —— 23. 缓冲信道和工作池(Buffered Channels and Worker Pools)

Noluye ·
`为什么信道done是有必要的,不能去掉?` &lt;-done肯定不是多余的。 这里的go result(done)函数是在另一个协程中进行的,只会阻塞住当前的协程,不会阻塞主协程。所以12楼14楼<a href="/user/skyfly" title="@skyfly">@skyfly</a> 解释不正确。 15楼通过去掉result的go让其阻塞主协程也ok。不过没有解释到&lt;-done的原因。 ... ... 文中代码尝试去掉 &lt;-done还会运行成功0~99的id数据,主要原因是跑在主协程的createWorkerPool(noOfWorkers)中wait函数会等所有携程执行完毕,而所有携程执行完毕则表示所有的数据已经从 jobs搬运到results中。 由于go result(done)函数执行很快没什么耗时,所以go result(done)协程几乎是和主协程同步完成。 **那是不是在go result(done)模拟耗时场景就能复现问题了? bingo** **函数提前结束,而协程go result(done)还未执行完毕。** 问题场景复现如下: 1、去掉30行代码time.Sleep(2 * time.Second) 主要是为了快速调试减少不必要耗时 2、58行后添加time.Sleep(1 * time.Second) 模拟耗时场景 3、65行代码noOfJobs := 12 原因同1 此时观察去掉&lt;-done不阻塞的情况下,主协程会提前结束。 加上&lt;-done, 会打印12条数据,程序正常。
#17
更多评论
func allocate(noOfJobs int) { for i := 0; i &lt; noOfJobs; i++ { randomno := rand.Intn(999) job := Job{i, randomno} jobs &lt;- job } close(jobs) } 如果for循环结束后,jobs里面的数据worker还来不及取走,这时执行到close,会不会导致works取数据失败?或者取不到足额的任务? 谢谢!!
#1
这里 `close` 和关闭文件的概念不同。 golang 里的 `close` 只是用于通知信道的接收方,所有数据都已经发送完毕,信道**没有真正关闭**。 若用 `for range` 接收数据时,对于关闭了的信道,会接收完剩下的有效数据,并退出循环。如果没有 `close` 提示数据发送完毕的话,`for range` 会接收完剩下所有有效数据后发生阻塞。 所以接收方 `worker` 是可以把 `jobs` 剩下的数据取走的。后面垃圾收集器会自动回收掉该信道的内存。 可以参见《[the go programming language](https://www.amazon.com/dp/0134190440/?tag=stackoverflow17-20)》的说法: &gt; You needn&#39;t close every channel when you&#39;ve finished with it. It&#39;s only necessary to close a channel when it is important to tell the receiving goroutines that all data have been sent. A channel that the garbage collector determinies to be unreachable will have its resources reclaimed whether or not it is closed. (Don&#39;t confuse this with the close operation for open files. It is important to call the Close method on every file when you&#39;ve finished with it.)
#2