Golang Go语言中一段代码执行疑问,关于 defer 执行时机的问题

发布于 1周前 作者 nodeper 来自 Go语言

问一个 Go 程序的执行问题

下面的代码中,我虽然知道了 1s 之后,会执行 case <-ctx.Done() 这个 case 。但是我想搞清楚原理:

我有一个疑问:

  1. defer cancel() 是需要等到 main 函数返回才可以执行
  2. 从程序代码中看,下面的程序中,执行 select 的时候,都会阻塞住才对
  3. 如果 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

7 回复

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 &lt;-time.After(2 * time.Second):<br> fmt.Println("overslept")<br> case &lt;-ctx.Done():<br> fmt.Println(ctx.Err())<br> }<br> fmt.Println(time.Since(timeNow))<br><br>}<br><br>

咦,玩 V 站不久,回复里面竟然无法使用 markdown 格式

在Go语言中,defer语句用于延迟一个函数或方法的执行,直到包含该defer语句的函数执行完毕。这通常用于资源清理、文件关闭、解锁互斥锁等操作,以确保这些操作总是会在函数返回前被执行,无论函数是正常结束还是由于错误提前退出。

关于defer的执行时机,以下几点值得注意:

  1. 栈后进先出defer语句的执行遵循后进先出的原则(LIFO)。也就是说,最后出现的defer会最先被执行。

  2. 函数返回前执行defer语句中的函数会在包含它的函数即将返回前执行。这意味着,即使函数中有return语句,defer中的代码也会在该return语句实际执行并返回结果之前运行。

  3. 参数求值时机:虽然defer的函数调用是延迟的,但其参数是在defer语句被遇到时立即求值的。这意味着,如果defer语句中包含函数调用或变量引用,这些操作会在defer语句被解析时执行,而不是在defer的函数实际执行时。

  4. 错误处理defer常用于错误处理,确保资源在发生错误时也能被正确释放。

如果你在具体代码中有关于defer执行时机的疑问,建议检查defer语句的使用位置、参数求值以及是否有其他控制流语句(如returnbreakcontinue)影响了其执行顺序。

回到顶部