英文:
Product of two time duration is zero in a Golang variable
问题
在下面的Golang代码中,奇怪的是两个时间持续时间的乘积在变量'delay'中为零,但是当直接打印乘积而不经过任何变量时,输出结果是预期的。有人能解释一下吗?
func StartCleanTask() {
go func() {
delay := cfg.Config.Timeout * time.Second
for {
fmt.Println("Go clean task: ", delay, cfg.Config.Timeout*time.Second)
select {
case <-time.After(cfg.Config.Timeout * time.Second):
clean()
}
}
}()
}
输出结果为:
Go clean task: 0 5m0s
更新:
我还尝试运行了以下代码,它运行良好。
package main
import "fmt"
import "time"
func main() {
var timeout time.Duration
timeout = 100
delay := timeout * time.Second
fmt.Println("Go clean task: ", delay, timeout*time.Second)
}
再次更新:
Paul的答案被接受。实际上,StartCleanTask()在cfg包的init函数中被调用,并且cfg.Config.Timeout在main函数中被赋予了一个指定的值。但是我忽略了包的init()函数在main()之前被隐式调用,所以变量delay始终为零。
顺便说一下,我不明白为什么有些人给这个问题打负分。我认为其他人可能会遇到类似的问题,这篇文章对于那些真正不了解init()和main()之间调用顺序的受害者应该是有帮助的。
英文:
It's weird in below Golang code that product of two time duration is zero in variable 'delay', but when print the product directly without through any variable, the output is as expected. Anyone can explain this?
func StartCleanTask() {
go func() {
delay := cfg.Config.Timeout * time.Second
for {
fmt.Println("Go clean task: ", delay, cfg.Config.Timeout*time.Second)
select {
case <-time.After(cfg.Config.Timeout * time.Second):
clean()
}
}
}()
}
The output is:
Go clean task: 0 5m0s
Update:
I also attempted to run following code, it works well.
package main
import "fmt"
import "time"
func main() {
var timeout time.Duration
timeout = 100
delay := timeout * time.Second
fmt.Println("Go clean task: ", delay, timeout*time.Second)
}
Update again:
Paul's answer is accepted. Actually StartCleanTask() is invoked in the init function of cfg package, and cfg.Config.Timeout is assigned to a specified value in main function. BUT I ignored that package's init() function is invoked implicitly before main(), so the variable delay is always ZERO.
BTW, I don't understand why some people give negative score to this question. I think others may hit similar problem, and this post should be helpful to the victim who don't really know the calling sequence between init() and main().
答案1
得分: 2
我猜测你正在并发地更新cfg.Config.Timeout
,可能是这样的:
func main() {
StartCleanTask()
cfg.Config.Timeout = 300
...
}
这引入了一个竞争条件,因为在StartCleanTask
内部的goroutine中,delay
在Timeout
初始化之前被赋值,而fmt.Println
发生在初始化之后。
你可以使用竞争检测器来查看是否存在这个问题。
假设cfg
只被初始化一次,可能正确的修复方法是在初始化完成后才启动清理任务。
英文:
My guess is that you're updating cfg.Config.Timeout
concurrently to this task. Perhaps like this:
func main() {
StartCleanTask()
cfg.Config.Timeout = 300
...
}
This introduces a race, and it happens that in the goroutine inside StartCleanTask
, delay
is assigned before the Timeout
is initialized, and the fmt.Println
happens after the initialization.
You can use the race detector to see if this is the problem.
Assuming that cfg
is only initialized once, probably the right fix is to only start the cleanup task once the initialization has finished.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论