3 回答
TA贡献1946条经验 获得超3个赞
您的ScheduleWorks
函数在主协程(即运行该main()
函数的协程,程序在该协程中启动)中通过input
. AWorker
接收它,并通过 发送另一个值output
。但是此时没有人接收output
,因此程序无法继续,主 goroutine 将下一个值发送到另一个Worker
.
对每个 Worker 重复这个推理。你有runtime.NumCPU()
工人,这可能少于numJobs
. 比方说runtime.NumCPU() == 4
,所以你有 4 个工人。最后,您已成功发送 4 个值,每个值都是一对一的Worker
。由于没有人从 读取output
,所有 Worker 都忙于尝试发送,因此它们无法通过 接受更多数据input
,因此第五个input <- i
将挂起。此时每个 goroutine 都在等待;这就是僵局。
您会注意到,如果您启动 20 个或更多的 Worker 而不是 runtime.NumCPU()
,则该程序可以运行。那是因为主 goroutine 可以通过 发送它想要的所有内容input
,因为有足够的工作人员来接收它们。
如果不是所有这些,而是将input <- i
循环放在另一个 goroutine 中,就像在您成功的示例中一样,main
goroutine(在其中ScheduleWorks
运行)可以继续并从output
. 所以,每次这个新的 goroutine 发送一个值时,worker 发送另一个值,output
主 goroutine 得到这个输出,worker 可以接收另一个值。没有人等待,程序成功了。
TA贡献1824条经验 获得超8个赞
这是因为 Go 中的所有内容默认都是阻塞的。
当您在无缓冲通道上发送第一个值时,它会阻塞,直到接收器从通道中取出该值。
可以通过添加“容量”来缓冲通道。
例如:
make(chan int, 20) // Make a buffered channel of int with capacity 20
从Go 规范:
容量(以元素数为单位)设置通道中缓冲区的大小。如果容量大于零,则通道是异步的:如果缓冲区未满(发送)或非空(接收),则通信操作成功而不会阻塞,并且元素按发送顺序接收。如果容量为零或不存在,则只有当发送方和接收方都准备好时,通信才能成功。
您可以通过使用缓冲通道而不是非缓冲通道来使原始函数工作,但是将函数调用包装在 goroutine 中可能是更好的方法,因为它实际上是并发的。
来自Effective Go(完整阅读此文档!它可能是 Stack Overflow 上 Go 答案中链接最多的文档):
接收器总是阻塞直到有数据要接收。如果通道未缓冲,则发送方将阻塞,直到接收方收到该值。如果通道有缓冲区,则发送方只会阻塞,直到值被复制到缓冲区;如果缓冲区已满,这意味着等待某个接收器检索到一个值。
如果您使用缓冲通道,那么您只是填充通道,继续前进,然后再次排空。不能同时进行。
例子:
改变
input, output := make(chan int), make(chan int)
到
input, output := make(chan int, 20), make(chan int, 20)
- 3 回答
- 0 关注
- 214 浏览
添加回答
举报