bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

golang中defer的實(shí)現(xiàn)原理

golang中defer的實(shí)現(xiàn)原理,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

目前創(chuàng)新互聯(lián)已為成百上千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間、網(wǎng)站托管維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、湞江網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

defer是golang提供的關(guān)鍵字,在函數(shù)或者方法執(zhí)行完成,返回之前調(diào)用。
每次defer都會(huì)將defer函數(shù)壓入棧中,調(diào)用函數(shù)或者方法結(jié)束時(shí),從棧中取出執(zhí)行,所以多個(gè)defer的執(zhí)行順序是先入后出。

for i := 0; i <= 3; i++ {
    defer fmt.Print(i)
}
//輸出結(jié)果時(shí) 3,2,1,0

defer的觸發(fā)時(shí)機(jī)

官網(wǎng)說(shuō)的很清楚:
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.

  1. 包裹著defer語(yǔ)句的函數(shù)返回時(shí)

  2. 包裹著defer語(yǔ)句的函數(shù)執(zhí)行到最后時(shí)

  3. 當(dāng)前goroutine發(fā)生Panic時(shí)

        //輸出結(jié)果:return前執(zhí)行defer
       func f1() {
           defer fmt.Println("return前執(zhí)行defer")
           return 
       }
    
       //輸出結(jié)果:函數(shù)執(zhí)行
       // 函數(shù)執(zhí)行到最后
       func f2() {
           defer fmt.Println("函數(shù)執(zhí)行到最后")
           fmt.Println("函數(shù)執(zhí)行")
       }
    
       //輸出結(jié)果:panic前  第一個(gè)defer在Panic發(fā)生時(shí)執(zhí)行,第二個(gè)defer在Panic之后聲明,不能執(zhí)行到
       func f3() {
           defer fmt.Println("panic前")
           panic("panic中")
           defer fmt.Println("panic后")
       }

defer,return,返回值的執(zhí)行順序

先來(lái)看3個(gè)例子

func f1() int { //匿名返回值
        var r int = 6
        defer func() {
                r *= 7
        }()
        return r
}

func f2() (r int) { //有名返回值
        defer func() {
                r *= 7
        }()
        return 6
}

func f3() (r int) { //有名返回值
    defer func(r int) {
        r *= 7
    }(r)
    return 6
}

f1的執(zhí)行結(jié)果是6, f2的執(zhí)行結(jié)果是42,f3的執(zhí)行結(jié)果是6
在golang的官方文檔里面介紹了,return,defer,返回值的執(zhí)行順序:
if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller.

1. 先給返回值賦值
2. 執(zhí)行defer語(yǔ)句
3. 包裹函數(shù)return返回

f1的結(jié)果是6。f1是匿名返回值,匿名返回值是在return執(zhí)行時(shí)被聲明,因此defer聲明時(shí),還不能訪問(wèn)到匿名返回值,defer的修改不會(huì)影響到返回值。
f2先給返回值r賦值,r=6,執(zhí)行defer語(yǔ)句,defer修改r, r = 42,然后函數(shù)return。
f3是有名返回值,但是因?yàn)閞是作為defer的傳參,在聲明defer的時(shí)候,就進(jìn)行參數(shù)拷貝傳遞,所以defer只會(huì)對(duì)defer函數(shù)的局部參數(shù)有影響,不會(huì)影響到調(diào)用函數(shù)的返回值。

閉包與匿名函數(shù)
匿名函數(shù):沒(méi)有函數(shù)名的函數(shù)。
閉包:可以使用另外一個(gè)函數(shù)作用域中的變量的函數(shù)。

for i := 0; i <= 3; i++ {
    defer func() {
        fmt.Print(i)
    }
}
//輸出結(jié)果時(shí) 3,3,3,3
因?yàn)閐efer函數(shù)的i是對(duì)for循環(huán)i的引用,defer延遲執(zhí)行,for循環(huán)到最后i是3,到defer執(zhí)行時(shí)i就 
是3

for i := 0; i <= 3; i++ {
    defer func(i int) {
        fmt.Print(i)
    }(i)
}
//輸出結(jié)果時(shí) 3,2,1,0
因?yàn)閐efer函數(shù)的i是在defer聲明的時(shí)候,就當(dāng)作defer參數(shù)傳遞到defer函數(shù)中

defer源碼解析
defer的實(shí)現(xiàn)源碼是在runtime.deferproc
然后在函數(shù)返回之前的地方,運(yùn)行函數(shù)runtime.deferreturn。
先了解defer結(jié)構(gòu)體:

    type _defer struct {
            siz     int32 
            started bool
            sp      uintptr // sp at time of defer
            pc      uintptr
            fn      *funcval
            _panic  *_panic // panic that is running defer
            link    *_defer
    }

sp 和 pc 分別指向了棧指針和調(diào)用方的程序計(jì)數(shù)器,fn是向 defer 關(guān)鍵字中傳入的函數(shù),Panic是導(dǎo)致運(yùn)行defer的Panic。
每遇到一個(gè)defer關(guān)鍵字,defer函數(shù)都會(huì)被轉(zhuǎn)換成runtime.deferproc
deferproc通過(guò)newdefer創(chuàng)建一個(gè)延遲函數(shù),并將這個(gè)新建的延遲函數(shù)掛在當(dāng)前goroutine的_defer的鏈表上

    func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
            sp := getcallersp()
            argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
            callerpc := getcallerpc()

            d := newdefer(siz)
            if d._panic != nil {
                    throw("deferproc: d.panic != nil after newdefer")
            }
            d.fn = fn
            d.pc = callerpc
            d.sp = sp
            switch siz {
            case 0:
                    // Do nothing.
            case sys.PtrSize:
                    *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
            default:
                    memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
            }
            return0()
    }

newdefer會(huì)先從sched和當(dāng)前p的deferpool取出一個(gè)_defer結(jié)構(gòu)體,如果deferpool沒(méi)有_defer,則初始化一個(gè)新的_defer。
_defer是關(guān)聯(lián)到當(dāng)前的g,所以defer只對(duì)當(dāng)前g有效。
d.link = gp._defer
gp._defer = d //用鏈表連接當(dāng)前g的所有defer

    func newdefer(siz int32) *_defer {
            var d *_defer
            sc := deferclass(uintptr(siz))
            gp := getg()
            if sc < uintptr(len(p{}.deferpool)) {
                    pp := gp.m.p.ptr()
                    if len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil { 
                            .....
                            d := sched.deferpool[sc]
                            sched.deferpool[sc] = d.link
                            d.link = nil
                            pp.deferpool[sc] = append(pp.deferpool[sc], d)
                    }
                    if n := len(pp.deferpool[sc]); n > 0 {
                            d = pp.deferpool[sc][n-1]
                            pp.deferpool[sc][n-1] = nil
                            pp.deferpool[sc] = pp.deferpool[sc][:n-1]
                    }
            }
            ......
            d.siz = siz
            d.link = gp._defer
            gp._defer = d
            return d
    }

deferreturn 從當(dāng)前g取出_defer鏈表執(zhí)行,每個(gè)_defer調(diào)用freedefer釋放_(tái)defer結(jié)構(gòu)體,并將該_defer結(jié)構(gòu)體放入當(dāng)前p的deferpool中。

defer性能分析
defer在開(kāi)發(fā)中,對(duì)于資源的釋放,捕獲Panic等很有用處。可以有些開(kāi)發(fā)者沒(méi)有考慮過(guò)defer對(duì)程序性能的影響,在程序中濫用defer。
在性能測(cè)試中可以發(fā)現(xiàn),defer對(duì)性能還是有一些影響。雨痕的Go 性能優(yōu)化技巧 4/1,對(duì)defer語(yǔ)句帶來(lái)的額外開(kāi)銷有一些測(cè)試。

測(cè)試代碼

    var mu sync.Mutex
    func noDeferLock() {
        mu.Lock()
        mu.Unlock()
    }   

    func deferLock() {
        mu.Lock()
        defer mu.Unlock()
    }          
    
    func BenchmarkNoDefer(b *testing.B) {
        for i := 0; i < b.N; i++ {
            noDeferLock()
        }
    }
    
    func BenchmarkDefer(b *testing.B) {
        for i := 0; i < b.N; i++ {
            deferLock()
    }

測(cè)試結(jié)果:

    BenchmarkNoDefer-4      100000000               11.1 ns/op
    BenchmarkDefer-4        36367237                33.1 ns/op

通過(guò)前面的源碼解析可以知道,defer會(huì)先調(diào)用deferproc,這些都會(huì)進(jìn)行參數(shù)拷貝,deferreturn還會(huì)提取相關(guān)信息延遲執(zhí)行,這些都是比直接call一條語(yǔ)句消耗更大。

defer性能不高,每次defer耗時(shí)20ns,,在一個(gè)func內(nèi)連續(xù)出現(xiàn)多次,性能消耗是20ns*n,累計(jì)出來(lái)浪費(fèi)的cpu資源很大的。

解決之道:除了需要異常捕獲時(shí),必須使用defer;其它資源回收類defer,可以判斷失敗后,使用goto跳轉(zhuǎn)到資源回收的代碼區(qū)。對(duì)于競(jìng)爭(zhēng)資源,可以在使用完之后,立馬釋放資源,這樣才能最優(yōu)的使用競(jìng)爭(zhēng)資源。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。

本文題目:golang中defer的實(shí)現(xiàn)原理
標(biāo)題路徑:http://vcdvsql.cn/article4/gjisie.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)品牌網(wǎng)站設(shè)計(jì)微信小程序移動(dòng)網(wǎng)站建設(shè)App設(shè)計(jì)響應(yīng)式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化