Golang Go语言中一段代码执行疑问,关于 defer 执行时机的问题
问一个 Go 程序的执行问题
下面的代码中,我虽然知道了 1s 之后,会执行 case <-ctx.Done() 这个 case 。但是我想搞清楚原理:
我有一个疑问:
- defer cancel() 是需要等到 main 函数返回才可以执行
- 从程序代码中看,下面的程序中,执行 select 的时候,都会阻塞住才对
- 如果 select 都会阻塞住的话,那就没有办法执行 return 了,如果没有执行 return 的话,那就不应该执行 defer 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()
select {
case <-time.After(2 * time.Second):
fmt.Println(“overslept”)
case <-ctx.Done():
fmt.Println(ctx.Err())
}
fmt.Println(time.Since(timeNow))
}
Golang Go语言中一段代码执行疑问,关于 defer 执行时机的问题
更多关于Golang Go语言中一段代码执行疑问,关于 defer 执行时机的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
1s 后
case <-ctx.Done():
fmt.Println(ctx.Err())
会被执行 select 执行完成 -> main 函数退出
更多关于Golang Go语言中一段代码执行疑问,关于 defer 执行时机的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
select 在 case <-ctx.Done():这行就满足条件了吧,然后 return 了。
return 之后再调用了 cancel()
想说看文档结果发现这段程序就是文档,那你继续看一下 WithTimeout 和关联的 WithDeadline 的文档里的描述,这里并没有什么原理,就只是文档描述的行为
另外看你的描述,如果你认为代码的执行顺序是 1 秒后执行了 cancel(),然后 select 进第二个 case ,我建议你把 defer cancel()这部分改成
defer func(){
LOGSOMETHING;
cancel()
}()
确认一下执行顺序,再看一下文档理解一下
这里的 context.WithTimeout 会启动一个 goroutine ,在 1s 之后 canal 掉这个 ctx
这里的 select 无论如何都不会阻塞,无论是 1s 之后的 ctx.Done() 还是 2s 之后这个 timer 会返回,都会让这个 select 继续运行…
谢谢你,程序改为下面的这样的,就清晰很多了。
结论就是你说的,不是 defer cancel() 让 -ctx.Done()。而是时间到了之后,ctx.Done 里面就会有消息写入了。
defer cancel() 其实就是一个兜底的策略,可以取保 main 返回的时候,可以 cancel 掉<br>package main<br><br>import (<br> "context"<br> "fmt"<br> "time"<br>)<br><br>const shortDuration = 1 * time.Second<br><br>func main() {<br> timeNow := time.Now()<br> ctx, cancel := context.WithTimeout(context.Background(), shortDuration)<br> //defer cancel()<br> defer func() {<br> time.Sleep(5 * time.Second)<br> fmt.Println(1111)<br> cancel()<br> }()<br> select {<br> case <-time.After(2 * time.Second):<br> fmt.Println("overslept")<br> case <-ctx.Done():<br> fmt.Println(ctx.Err())<br> }<br> fmt.Println(time.Since(timeNow))<br><br>}<br><br>
咦,玩 V 站不久,回复里面竟然无法使用 markdown 格式
在Go语言中,defer
语句用于延迟一个函数或方法的执行,直到包含该defer
语句的函数执行完毕。这通常用于资源清理、文件关闭、解锁互斥锁等操作,以确保这些操作总是会在函数返回前被执行,无论函数是正常结束还是由于错误提前退出。
关于defer
的执行时机,以下几点值得注意:
-
栈后进先出:
defer
语句的执行遵循后进先出的原则(LIFO)。也就是说,最后出现的defer
会最先被执行。 -
函数返回前执行:
defer
语句中的函数会在包含它的函数即将返回前执行。这意味着,即使函数中有return
语句,defer
中的代码也会在该return
语句实际执行并返回结果之前运行。 -
参数求值时机:虽然
defer
的函数调用是延迟的,但其参数是在defer
语句被遇到时立即求值的。这意味着,如果defer
语句中包含函数调用或变量引用,这些操作会在defer
语句被解析时执行,而不是在defer
的函数实际执行时。 -
错误处理:
defer
常用于错误处理,确保资源在发生错误时也能被正确释放。
如果你在具体代码中有关于defer
执行时机的疑问,建议检查defer
语句的使用位置、参数求值以及是否有其他控制流语句(如return
、break
、continue
)影响了其执行顺序。