GO中的defer會在當前函數(shù)返回前執(zhí)行傳入的函數(shù),常用于關閉文件描述符,關閉鏈接及解鎖等操作。
創(chuàng)新互聯(lián)是一家專業(yè)提供朝陽縣企業(yè)網(wǎng)站建設,專注與成都網(wǎng)站建設、做網(wǎng)站、html5、小程序制作等業(yè)務。10年已為朝陽縣眾多企業(yè)、政府機構等服務。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡公司優(yōu)惠進行中。
Go語言中使用defer時會遇到兩個常見問題:
接下來我們來詳細處理這兩個問題。
官方有段對defer的解釋:
這里我們先來一道經(jīng)典的面試題
你覺得這個會打印什么?
輸出結果:
這里是遵循先入后出的原則,同時保留當前變量的值。
把這道題簡化一下:
輸出結果
上述代碼輸出似乎不符合預期,這個現(xiàn)象出現(xiàn)的原因是什么呢?經(jīng)過分析,我們發(fā)現(xiàn)調用defer關鍵字會立即拷貝函數(shù)中引用的外部參數(shù),所以fmt.Println(i)的這個i是在調用defer的時候就已經(jīng)賦值了,所以會直接打印1。
想要解決這個問題也很簡單,只需要向defer關鍵字傳入匿名函數(shù)
這里把一些垃圾回收使用的字段忽略了。
中間代碼生成階段cmd/compile/internal/gc/ssa.go會處理程序中的defer,該函數(shù)會根據(jù)條件不同,使用三種機制來處理該關鍵字
開放編碼、堆分配和棧分配是defer關鍵字的三種方法,而Go1.14加入的開放編碼,使得關鍵字開銷可以忽略不計。
call方法會為所有函數(shù)和方法調用生成中間代碼,工作內(nèi)容:
defer關鍵字在運行時會調用deferproc,這個函數(shù)實現(xiàn)在src/runtime/panic.go里,接受兩個參數(shù):參數(shù)的大小和閉包所在的地址。
編譯器不僅將defer關鍵字轉成deferproc函數(shù),還會通過以下三種方式為所有調用defer的函數(shù)末尾插入deferreturn的函數(shù)調用
1、在cmd/compile/internal/gc/walk.go的walkstmt函數(shù)中,在遇到ODEFFER節(jié)點時會執(zhí)行Curfn.Func.SetHasDefer(true),設置當前函數(shù)的hasdefer屬性
2、在ssa.go的buildssa會執(zhí)行s.hasdefer = fn.Func.HasDefer()更新hasdefer
3、在exit中會根據(jù)hasdefer在函數(shù)返回前插入deferreturn的函數(shù)調用
runtime.deferproc為defer創(chuàng)建了一個runtime._defer結構體、設置它的函數(shù)指針fn、程序計數(shù)器pc和棧指針sp并將相關參數(shù)拷貝到相鄰的內(nèi)存空間中
最后調用的return0是唯一一個不會觸發(fā)延遲調用的函數(shù),可以避免deferreturn的遞歸調用。
newdefer的分配方式是從pool緩存池中獲取:
這三種方式取到的結構體_defer,都會被添加到鏈表的隊頭,這也是為什么defer按照后進先出的順序執(zhí)行。
deferreturn就是從鏈表的隊頭取出并調用jmpdefer傳入需要執(zhí)行的函數(shù)和參數(shù)。
該函數(shù)只有在所有延遲函數(shù)都執(zhí)行后才會返回。
如果我們能夠將部分結構體分配到棧上就可以節(jié)約內(nèi)存分配帶來的額外開銷。
在call函數(shù)中有在棧上分配
在運行期間deferprocStack只需要設置一些未在編譯期間初始化的字段,就可以將棧上的_defer追加到函數(shù)的鏈表上。
除了分配的位置和堆的不同,其他的大致相同。
Go語言在1.14中通過開放編碼實現(xiàn)defer關鍵字,使用代碼內(nèi)聯(lián)優(yōu)化defer關鍵的額外開銷并引入函數(shù)數(shù)據(jù)funcdata管理panic的調用,該優(yōu)化可以將 defer 的調用開銷從 1.13 版本的 ~35ns 降低至 ~6ns 左右。
然而開放編碼作為一種優(yōu)化 defer 關鍵字的方法,它不是在所有的場景下都會開啟的,開放編碼只會在滿足以下的條件時啟用:
如果函數(shù)中defer關鍵字的數(shù)量多于8個或者defer處于循環(huán)中,那么就會禁用開放編碼優(yōu)化。
可以看到這里,判斷編譯參數(shù)不用-N,返回語句的數(shù)量和defer數(shù)量的乘積小于15,會啟用開放編碼優(yōu)化。
延遲比特deferBitsTemp和延遲記錄是使用開放編碼實現(xiàn)defer的兩個最重要的結構,一旦使用開放編碼,buildssa會在棧上初始化大小為8個比特的deferBits
延遲比特中的每一個比特位都表示該位對應的defer關鍵字是否需要被執(zhí)行。延遲比特的作用就是標記哪些defer關鍵字在函數(shù)中被執(zhí)行,這樣就能在函數(shù)返回時根據(jù)對應的deferBits確定要執(zhí)行的函數(shù)。
而deferBits的大小為8比特,所以該優(yōu)化的條件就是defer的數(shù)量小于8.
而執(zhí)行延遲調用的時候仍在deferreturn
這里做了特殊的優(yōu)化,在runOpenDeferFrame執(zhí)行開放編碼延遲函數(shù)
1、從結構體_defer讀取deferBits,執(zhí)行函數(shù)等信息
2、在循環(huán)中依次讀取執(zhí)行函數(shù)的地址和參數(shù)信息,并通過deferBits判斷是否要執(zhí)行
3、調用reflectcallSave執(zhí)行函數(shù)
1、新加入的defer放入隊頭,執(zhí)行defer時是從隊頭取函數(shù)調用,所以是后進先出
2、通過判斷defer關鍵字、return數(shù)量來判斷是否開啟開放編碼優(yōu)化
3、調用deferproc函數(shù)創(chuàng)建新的延遲調用函數(shù)時,會立即拷貝函數(shù)的參數(shù),函數(shù)的參數(shù)不會等到真正執(zhí)行時計算
舉個例子,如下
答案
解析:
defer函數(shù)在處理Panic() 和Recover()時的應用
panic 函數(shù)是內(nèi)置的go函數(shù),它 終止 go程序的當前流程并開始 panicking , recover 函數(shù)也是內(nèi)置的一個go函數(shù),允許你收回處理那些使用了 panic 函數(shù)的 goroutine 的控制權
來個案例
舉個例子,如果我們的代碼邏輯是下面這樣的:
打開數(shù)據(jù)庫連接
defer 關閉連接
defer 刪除數(shù)據(jù)
因為一般defer定義是和打開連接并列的,打開文件,打開連接之后就定義了defer, 如果這之后你的defer是基于這個連接做的事情,那么如果先進先執(zhí)行的話就會錯誤了。這就是當初Go設計defer的時候考慮的問題。
這里順帶提醒一下defer是存在一些小坑的,就是defer里面的變量是申明的時候就copy的,不會隨著后面的函數(shù)邏輯改變而改變,除非你用指針類型。
package main
import "fmt"
func main() {
var whatever [5]struct{}
for i := range whatever {
fmt.Println(i)
}
for i := range whatever {
defer func() { fmt.Println(i) }()
}
for i := range whatever {
defer func(n int) { fmt.Println(n) }(i)
}
}
本文主要介紹了Go語言中文件讀寫的相關操作。
文件是什么?
計算機中的文件是存儲在外部介質(通常是磁盤)上的數(shù)據(jù)集合,文件分為文本文件和二進制文件。
os.Open() 函數(shù)能夠打開一個文件,返回一個 *File 和一個 err 。對得到的文件實例調用 close() 方法能夠關閉文件。
為了防止文件忘記關閉,我們通常使用defer注冊文件關閉語句。
Read方法定義如下:
它接收一個字節(jié)切片,返回讀取的字節(jié)數(shù)和可能的具體錯誤,讀到文件末尾時會返回 0 和 io.EOF 。 舉個例子:
使用for循環(huán)讀取文件中的所有數(shù)據(jù)。
bufio是在file的基礎上封裝了一層API,支持更多的功能。
io/ioutil 包的 ReadFile 方法能夠讀取完整的文件,只需要將文件名作為參數(shù)傳入。
os.OpenFile() 函數(shù)能夠以指定模式打開文件,從而實現(xiàn)文件寫入相關功能。
其中:
name :要打開的文件名 flag :打開文件的模式。 模式有以下幾種:
perm :文件權限,一個八進制數(shù)。r(讀)04,w(寫)02,x(執(zhí)行)01。
文章名稱:go語言中defer go語言中的方法
文章轉載:http://www.rwnh.cn/article40/doppcho.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供關鍵詞優(yōu)化、網(wǎng)站內(nèi)鏈、企業(yè)網(wǎng)站制作、定制開發(fā)、企業(yè)建站、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)