網站首頁 編程語言 正文
什么是defer
defer用來聲明一個延遲函數,把這個函數放入到一個棧上, 當外部的包含方法return之前,返回參數到調用方法之前調用,也可以說是運行到最外層方法體的"}"時調用。我們經常用他來做一些資源的釋放,比如關閉io操作
func doSomething(fileName string) { file,err := os.Open(fileName) if err != nil { panic(err) } defer file.Close() }
defer 可以保證方法可以在外圍函數返回之前調用。有點像其他言的 try finally
try{ }finally{ }
Go語言defer預計算參數
Go 語言中所有的函數調用都是傳值的,雖然 defer 是關鍵字,但是也繼承了這個特性。假設我們想要計算 main 函數運行的時間,可能會寫出以下的代碼:
package main import ( "fmt" "time" ) func main() { startedAt := time.Now() defer fmt.Println(time.Since(startedAt)) time.Sleep(time.Second) //休眠一秒 }
結果是:
D:\workspace\go\src\test>go run main.go
0s?
運行結果并不符合我們的預期,這個現象背后的原因是什么呢?經過分析,我們會發現調用 defer 關鍵字會立刻拷貝函數中引用的外部參數,所以 time.Since(startedAt) 的結果不是在 main 函數退出之前計算的,而是在 defer 關鍵字調用時計算的【defer入棧的時候】,最終導致上述代碼輸出 0s
我們再來看個簡單例子來說明上述解釋:
package main import ( "fmt" ) func main() { i := 1 defer fmt.Println(test(i)) i = 100 } func test(i int) int { i = i + 1 return i } D:\workspace\go\src\test>go run main.go 2
當代碼運行到defer fmt.Println(test(i))的時候,會把defer右邊最外層函數的參數計算完畢,并傳遞進函數里,但不會執行函數體的代碼直到包裹defer的函數返回。我們先看會把defer右邊最外層函數的參數計算完畢,并傳遞進函數里這句話,對應例子就是先把test(i)算出來,此時i=1,計算test(1)得2,然后fmt.Println(2)入棧,等到最后程序運行完了再運行defer結果就是2(但不會執行函數體的代碼直到包裹defer的函數返回)。
我們再來看一個例子與匿名函數結合:
package main import ( "fmt" ) func main() { i := 1 defer func() { fmt.Println(test(i)) }() i = 100 } func test(i int) int { i = i + 1 return i }
結果:
D:\workspace\go\src\test>go run main.go
101 ?
使用匿名函數,結果是101,相當于i給到test方法的是100,那為什么呢?還是那句話:但不會執行函數體的代碼直到包裹defer的函數返回
也就是說他會把整個{ fmt.Println(test(i)) }()函數體入棧,等到最后程序運行完了再運行defer,此時的i是100,運行test后就是101了。
所以你要解決第一個打印為0s的問題,你就可以使用匿名函數來解決,如下:
package main import ( "fmt" "time" ) func main() { startedAt := time.Now() defer func() { fmt.Println(time.Since(startedAt)) }() time.Sleep(time.Second) //休眠一秒 }
結果:
D:\workspace\go\src\test>go run main.go
1.0152825s
總結
原文鏈接:https://juejin.cn/post/7075529260315705380/
相關推薦
- 2022-07-10 axios調用接口報錯
- 2022-08-20 C++詳細講解內存管理工具primitives_C 語言
- 2022-10-04 python?Pillow圖像降噪處理顏色處理_python
- 2022-11-28 詳解如何使用C++寫一個線程安全的單例模式_C 語言
- 2022-05-28 Python裝飾器詳細介紹_python
- 2022-01-08 關于git操作warning: adding embedded git repository: pp
- 2022-06-19 C#中的checksum計算公式_C#教程
- 2022-11-09 Go語言數據結構之二叉樹可視化詳解_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支