上个月中旬,Go 1.23版本正式发布!这也是Russ Cox作为Go tech leader的最后一个发布版本,他本人在该版本中做出重要贡献,那就是解决了一直困扰Go团队的Timer/Ticker的GC回收问题,进而解决了Timer的Stop和Reset很难正确使用的问题。
不过,就在昨天,一个叫tulir的gopher提出的issue(ttps://github.com/golang/go/issues/69186)差点让Russ Cox“晚节不保”:)。该issue提到,他写的一段使用了Timer的代码在Go 1.22中工作正常,但在Go 1.23中就无法工作了,具体现象是:在linux上,整个程序hang住不动了,而在macOS上,则直接引发panic异常退出:"fatal error: ts set in timer"。
随即,Go101老貘兄“补上一刀”,给出了一个更为简洁的示例:
package main
# 堆代码 duidaima.com
import "time"
func main() {
illegalTimerCopy := *time.NewTimer(time.Second)
illegalTimerCopy.Stop() // block for ever
}
我也实测了该示例,在我的macOS上,用go1.23.0运行,直接panic,即便使用GODEBUG=synctimerchan=1退回到Go 1.23以前的行为也不行。在centos 7.9(kernel 3.10)上跑,也发生了和issue一样的现象:hang住不动。到这里,我也不得不认为:这是go 1.23 Timer引入的新bug!但真相果真如此吗?几个小时后,Go大神Ian Lance Taylor现身说法了。他居然表示对Go 1.23之前的版本依然可以正确运行上述代码表示“惊讶”。
之后,他表示从Go 1.4版本开始,Go标准库文档(#8776)就对Timer类型的使用做出了限制:"A Timer must be created with NewTimer or AfterFunc.",即Timer只能使用NewTimer和AfterFunc创建。
而像上述代码中的对Timer实例的Copy的行为则是未定义的。
目前Ian Lance Taylor将该issue改名为“proposal: cmd/vet: warn about copying a time.Timer value”,即建议在vet中增加warning,但他也不保证该proposal能被accept,因为要看后续是否会受到很多类似的问题报告。盖棺定论了,原来是我们的文档看的还不够仔细:)。
不过,即便go vet不增加对Timer copy的warning,我也想建议官方修改一下Timer的doc描述,再直白一些,就像对sync.Mutex那样:A Mutex must not be copied after first use.
在Timer文档中,也补充依据:A Timer must not be copied after being created.
你说呢?