关于Go并发素数筛中prime := <-ch先于go Filter()执行的疑问
解答Go并发素数筛的通道数据流动疑问
这是个非常好的问题,刚好戳中了Go并发模型里通道阻塞特性和goroutine调度的核心逻辑,咱们一步步拆解这个素数筛的执行流程,你就能明白为什么Filter能拿到数据了。
先明确核心前提:Go通道的同步阻塞特性
在Go里,通道的<-操作是同步且阻塞的:
- 当你执行
ch <- x时,发送方会暂停,直到有接收方准备好接收这个值; - 当你执行
x := <-ch时,接收方会暂停,直到通道里有数据可以接收。
这个特性是整个并发素数筛能正常工作的基础。
逐步骤拆解代码执行流程
咱们从主函数的第一次循环开始,跟着数据流动走:
初始启动与第一个素数的获取
- 主函数创建初始通道
ch,启动go Generate(ch)。Generate函数立刻开始执行,i=2时执行ch <- 2,但此时没有接收方,Generate会阻塞在这里。 - 主函数进入循环,执行
prime := <-ch,接收了通道里的2——这时候Generate的阻塞解除,立刻继续执行i=3,并尝试ch <- 3,但此时还没有其他接收方,Generate又一次阻塞。
- 主函数创建初始通道
Filter goroutine的启动与数据接收
- 主函数打印完2,创建新通道
ch1,启动go Filter(ch, ch1, prime)(这里prime是2)。 - Filter goroutine启动后,第一行代码就是
num := <-in(in就是原来的ch),这时候它会尝试接收通道里的3——刚好Generate之前阻塞在ch <-3,现在有了接收方,Generate的阻塞解除,继续生成下一个数字4,执行ch <-4后再次阻塞。
- 主函数打印完2,创建新通道
Filter的过滤与数据传递
- Filter拿到3后,检查
3 % 2 != 0,于是执行out <-3(out是ch1),但此时ch1还没有接收方,Filter会阻塞在这里。 - 主函数执行
ch = ch1,回到循环开头,执行prime := <-ch,接收ch1里的3——Filter的阻塞解除,继续执行下一轮num := <-in,接收Generate发的4。 - Filter检查
4%2 ==0,直接跳过,不往ch1发送,然后继续执行num := <-in,等待Generate的下一个数字。
- Filter拿到3后,检查
后续循环的逻辑复用
- 接下来的流程和上面完全一致:Generate持续生成数字,每个数字都会被对应的Filter接收、过滤,只有不能被当前素数整除的数字才会传递到下一层通道,最终被主函数接收并打印为新的素数。
解答你的核心疑问
你担心prime := <-ch在go Filter()之前,会把通道里的所有数据都取走,但实际上:
- 主函数只接收一个素数就会切换到新的通道(
ch = ch1),后续不会再从原来的通道接收数据; - Generate是持续生成新数字的,主函数接收第一个素数后,Generate立刻会把下一个数字发送到原通道,而Filter启动后会立刻从原通道接收这个数字(以及后续的数字);
- 通道的阻塞特性保证了数据不会“丢失”或“被抢”,每个发送操作都会匹配到对应的接收操作——要么是主函数的第一次接收,要么是Filter的接收。
举个简单的例子:主函数拿走了2,Generate立刻把3塞到原通道,这时候Filter刚好启动,就会把3拿走并处理,主函数接下来只会从新通道拿3,不会再碰原通道的后续数字(比如4、5等)。
内容的提问来源于stack exchange,提问作者offvw
相关产品推荐
相关产品推荐

