Go 1.16 组合通道迭代器时接收匿名struct值未更新问题排查
Golang 1.16 下组合通道迭代器时死循环问题
问题描述
在Golang 1.16环境下,为了遍历字符串的rune,编写了如下基础迭代器函数:
func iterRunes(s string) <-chan rune { runes := make(chan rune) go func() { defer close(runes) for _, char := range s { runes <- char } }() return runes }
该迭代器可正常运行。后续需要为迭代值添加序号,又编写了枚举迭代器:
func enumRunes(runes <-chan rune) <-chan struct {int; rune} { eRunes := make(chan struct {int; rune}) go func() { defer close(eRunes) i := 0 for r := range runes { eRunes <- struct {int; rune} {i, r} i++ } }() return eRunes }
该枚举迭代器单独运行正常,但当以enumRunes(iterRunes(pattern))的方式组合使用时出现异常。
异常复现代码
异常版本1:
tmp := enumRunes(iterRunes(pattern)) z := <-tmp for z.int < utf8.RuneCountInString(pattern) { fmt.Println("z = <- tmp;") fmt.Println(z) z = <-tmp }
运行输出如下(程序陷入死循环):
... z = <- tmp; {0 0} {0 0} z = <- tmp; {0 0} {0 0} z = <- tmp; {0 0} {0 0} z = <- tmp; ...
表现为z的值始终为{0 0},没有被通道的新值更新。
正常版本对照:
tmp := iterRunes(pattern) z := <-tmp for qq := 0; qq < utf8.RuneCountInString(pattern); qq++ { fmt.Println("z = <- tmp;") fmt.Println(z) z = <-tmp }
该版本中z为rune类型,可正常运行无死循环。
问题解答
根本原因
程序死循环并不是因为通道接收到的新值没有赋值给z,而是对通道关闭后的接收行为和循环退出逻辑的错误使用:
- 无缓冲通道在发送完所有值后会被主动关闭,当从已关闭的通道中接收值时,接收操作不会阻塞,会立刻返回对应数据类型的零值。
- 示例中枚举迭代器返回的通道元素为匿名结构体类型,零值为
{0, 0}(int类型零值0,rune类型零值0)。当通道中所有有效rune值被取完后,后续所有z = <-tmp的操作都会立刻返回这个零值。 - 循环的退出条件是
z.int < utf8.RuneCountInString(pattern),当拿到零值时z.int为0,永远满足"小于字符串rune总数"的判断,导致循环永远无法退出,持续接收通道的零值打印,形成无限循环。
你看到的全为{0 0}的输出,正是通道耗尽后返回的零值状态,循环前期的正常取值过程被截断后,就会给人一种"z从未被更新"的错觉。
而对照的正常版本使用计数器qq控制循环次数,达到固定次数后就会退出循环,就算通道关闭拿到零值也不会陷入死循环。
修复方式
- 遍历通道优先使用
for v := range ch语法,这种写法会在通道关闭且所有值被接收完毕后自动退出循环,不会收到无意义的零值。对应代码修改为:tmp := enumRunes(iterRunes(pattern)) for z := range tmp { fmt.Println(z) } - 如果需要手动接收通道值,必须使用双返回值的接收方式,通过第二个返回值判断通道是否关闭:
tmp := enumRunes(iterRunes(pattern)) for { z, ok := <-tmp if !ok { break } fmt.Println(z) } - 建议避免使用无字段名的匿名结构体作为通道元素,显式定义字段名可以减少混淆,例如:
type EnumeratedRune struct { Index int Val rune } func enumRunes(runes <-chan rune) <-chan EnumeratedRune { eRunes := make(chan EnumeratedRune) // 其余逻辑保持不变 }
内容的提问来源于stack exchange,提问作者Swiatoslaw
相关产品推荐
相关产品推荐

