问一个 Go 程序的执行问题
下面的代码中,我虽然知道了 1s 之后,会执行 case <-ctx.Done() 这个 case 。但是我想搞清楚原理:
我有一个疑问:
package main
import (
"context"
"fmt"
"time"
)
const shortDuration = 1 * time.Second
func main() {
timeNow := time.Now()
ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
defer cancel()
select {
case <-time.After(2 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
fmt.Println(time.Since(timeNow))
}
结贴了,执行 case <-ctx.Done() 是因为时间到了,而不是 defer cancel() 被执行了
程序改为下面这样子,就清晰很多了,可以看到,就算没有执行 cancel(),也会执行 case <-ctx.Done()
package main
import (
"context"
"fmt"
"time"
)
const shortDuration = 1 * time.Second
func main() {
timeNow := time.Now()
ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
//defer cancel()
defer func() {
time.Sleep(5 * time.Second)
fmt.Println(1111)
cancel()
}()
select {
case <-time.After(2 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
fmt.Println(time.Since(timeNow))
}
1
9313841411 2023-06-30 11:31:51 +08:00
1s 后
case <-ctx.Done(): fmt.Println(ctx.Err()) 会被执行 select 执行完成 -> main 函数退出 |
2
zeonluang 2023-06-30 11:34:15 +08:00
select 在 case <-ctx.Done():这行就满足条件了吧,然后 return 了。
return 之后再调用了 cancel() |
3
mcfog 2023-06-30 11:35:41 +08:00
想说看文档结果发现这段程序就是文档,那你继续看一下 WithTimeout 和关联的 WithDeadline 的文档里的描述,这里并没有什么原理,就只是文档描述的行为
另外看你的描述,如果你认为代码的执行顺序是 1 秒后执行了 cancel(),然后 select 进第二个 case ,我建议你把 defer cancel()这部分改成 defer func(){ LOGSOMETHING; cancel() }() 确认一下执行顺序,再看一下文档理解一下 |
4
Trim21 2023-06-30 11:38:09 +08:00
这里的 context.WithTimeout 会启动一个 goroutine ,在 1s 之后 canal 掉这个 ctx
这里的 select 无论如何都不会阻塞,无论是 1s 之后的 ctx.Done() 还是 2s 之后这个 timer 会返回,都会让这个 select 继续运行... |
5
CarrieBauch OP @mcfog
谢谢你,程序改为下面的这样的,就清晰很多了。 结论就是你说的,不是 defer cancel() 让 -ctx.Done()。而是时间到了之后,ctx.Done 里面就会有消息写入了。 defer cancel() 其实就是一个兜底的策略,可以取保 main 返回的时候,可以 cancel 掉 ``` package main import ( "context" "fmt" "time" ) const shortDuration = 1 * time.Second func main() { timeNow := time.Now() ctx, cancel := context.WithTimeout(context.Background(), shortDuration) //defer cancel() defer func() { time.Sleep(5 * time.Second) fmt.Println(1111) cancel() }() select { case <-time.After(2 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) } fmt.Println(time.Since(timeNow)) } ``` |
6
CarrieBauch OP 咦,玩 V 站不久,回复里面竟然无法使用 markdown 格式
|