亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go panic和recover函數(shù)使用細(xì)節(jié)深入探究

 更新時間:2023年12月01日 10:11:08   作者:qiya  
這篇文章主要為大家介紹了Go?的panic和recover函數(shù)使用細(xì)節(jié)深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前情提要

關(guān)于 panic 的時機(jī),在上篇 姿勢篇 我們已經(jīng)學(xué)習(xí)到 panic 有三種誕生方式:

  • 程序猿主動:調(diào)用 panic( ) 函數(shù);
  • 編譯器的隱藏代碼:比如除零場景;
  • 內(nèi)核發(fā)送給進(jìn)程信號:比如非法地址訪問 ;

三種都?xì)w一到 panic( ) 函數(shù)的調(diào)用,指出 Go 的 panic 只是一個特殊的函數(shù)調(diào)用,是語言層面的處理。

知道了 panic 是怎么來的,下一步就該了解 panic 怎么去的?初學(xué) Go 的時候,奇伢心里也常常有些疑問:

  • panic 究竟是啥?是一個結(jié)構(gòu)體?還是一個函數(shù)?
  • 為什么 panic 會讓 Go 進(jìn)程退出的 ?
  • 為什么 recover 一定要放在 defer 里面才生效?
  • 為什么 recover 已經(jīng)放在 defer 里面,但是進(jìn)程還是沒有恢復(fù)?
  • 為什么 panic 之后,還能再 panic ?有啥影響?

今天深入到代碼原理,明確以上問題。

Go 源碼版本聲明 Go 1.13.5

_panic 數(shù)據(jù)結(jié)構(gòu)

看看 _panic 的數(shù)據(jù)結(jié)構(gòu):

// runtime/runtime2.go
// 關(guān)鍵結(jié)構(gòu)體
type _panic struct {
    argp      unsafe.Pointer
    arg       interface{}    // panic 的參數(shù)
    link      *_panic        // 鏈接下一個 panic 結(jié)構(gòu)體
    recovered bool           // 是否恢復(fù),到此為止?
    aborted   bool           // the panic was aborted
}

重點(diǎn)字段關(guān)注:

  • link 字段:一個指向 _panic 結(jié)構(gòu)體的指針,表明 _panic 和 _defer 類似,_panic 可以是一個單向鏈表,就跟 _defer 鏈表一樣;
  • recovered 字段:重點(diǎn)來了,所謂的 _panic 是否恢復(fù)其實(shí)就是看這個字段是否為 true,recover( ) 其實(shí)就是修改這個字段;

再看一下 goroutine 的兩個重要字段:

type g struct {
    // ...
    _panic         *_panic // panic 鏈表,這是最里的一個
    _defer         *_defer // defer 鏈表,這是最里的一個;
    // ...
}

從這里我們看出:_defer 和 _panic 鏈表都是掛在 goroutine 之上的。

什么時候會導(dǎo)致 _panic 鏈表上多個元素?

panic( ) 的流程下,又調(diào)用了 panic( ) 函數(shù)。

這里有個細(xì)節(jié)要注意了,怎么才能做到 panic( ) 流程里面再次調(diào)用 panic( ) ?

劃重點(diǎn):只能是在 defer 函數(shù)上,才有可能形成一個 _panic 鏈表。因為 panic( ) 函數(shù)內(nèi)只會執(zhí)行 _defer 函數(shù) !

recover 函數(shù)

為了方便講解,我們由簡單的開始分析,先看 recover 函數(shù)究竟做了什么?

defer func() {
    recover()
}()

recover 對應(yīng)了 runtime/panic.go 中的 gorecover 函數(shù)實(shí)現(xiàn)。

gorecover 函數(shù)

func gorecover(argp uintptr) interface{} {
    // 只處理 gp._panic 鏈表最新的這個 _panic;
    gp := getg()
    p := gp._panic
    if p != nil && !p.recovered && argp == uintptr(p.argp) {
        p.recovered = true
        return p.arg
    }
    return nil
}

這個函數(shù)可太簡單了:

  • 取出當(dāng)前 goroutine 結(jié)構(gòu)體;
  • 取出當(dāng)前 goroutine 的 _panic 鏈表最新的一個 _panic,如果是非 nil 值,則進(jìn)行處理;
  • 該 _panic 結(jié)構(gòu)體的 recovered 賦值 true,程序返回;

這就是 recover 函數(shù)的全部內(nèi)容,只給 _panic.recovered 賦值而已,不涉及代碼的神奇跳轉(zhuǎn)。而 _panic.recovered 的賦值是在 panic 函數(shù)邏輯中發(fā)揮作用。

panic 函數(shù)

panic 的實(shí)現(xiàn)在一個叫做 gopanic 的函數(shù),位于 runtime/panic.go 文件。

gopanic 函數(shù)

panic 機(jī)制最重要最重要的就是 gopanic 函數(shù)了,所有的 panic 細(xì)節(jié)盡在此。為什么 panic 會顯得晦澀,主要有兩個點(diǎn):

  • 嵌套 panic 的時候,gopanic 會有遞歸執(zhí)行的場景;
  • 程序指令跳轉(zhuǎn)并不是常規(guī)的函數(shù)壓棧,彈棧,在 recovery 的時候,是直接修改指令寄存器的結(jié)構(gòu)體,從而直接越過了 gopanic 后面的邏輯,甚至是多層 gopanic 遞歸的邏輯;
// runtime/panic.go
func gopanic(e interface{}) {
    // 在棧上分配一個 _panic 結(jié)構(gòu)體
    var p _panic
    // 把當(dāng)前最新的 _panic 掛到鏈表最前面
    p.link = gp._panic
    gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
    for {
        // 取出當(dāng)前最近的 defer 函數(shù);
        d := gp._defer
        if d == nil {
            // 如果沒有 defer ,那就沒有 recover 的時機(jī),只能跳到循環(huán)外,退出進(jìn)程了;
            break
        }
        // 進(jìn)到這個邏輯,那說明了之前是有 panic 了,現(xiàn)在又有 panic 發(fā)生,這里一定處于遞歸之中;
        if d.started {
            if d._panic != nil {
                d._panic.aborted = true
            }
            // 把這個 defer 從鏈表中摘掉;
            gp._defer = d.link
            freedefer(d)
            continue
        }
        // 標(biāo)記 _defer 為 started = true (panic 遞歸的時候有用)
        d.started = true
        // 記錄當(dāng)前 _defer 對應(yīng)的 panic
        d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
        // 執(zhí)行 defer 函數(shù)
        reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
        // defer 執(zhí)行完成,把這個 defer 從鏈表里摘掉;
        gp._defer = d.link
        // 取出 pc,sp 寄存器的值;
        pc := d.pc
        sp := unsafe.Pointer(d.sp)
        // 如果 _panic 被設(shè)置成恢復(fù),那么到此為止;
        if p.recovered {
            // 摘掉當(dāng)前的 _panic
            gp._panic = p.link
            // 如果前面還有 panic,并且是標(biāo)記了 aborted 的,那么也摘掉;
            for gp._panic != nil && gp._panic.aborted {
                gp._panic = gp._panic.link
            }
            // panic 的流程到此為止,恢復(fù)到業(yè)務(wù)函數(shù)堆棧上執(zhí)行代碼;
            gp.sigcode0 = uintptr(sp)
            gp.sigcode1 = pc
            // 注意:恢復(fù)的時候 panic 函數(shù)將從此處跳出,本 gopanic 調(diào)用結(jié)束,后面的代碼永遠(yuǎn)都不會執(zhí)行。
            mcall(recovery)
            throw("recovery failed") // mcall should not return
        }
    }
    // 打印錯誤信息和堆棧,并且退出進(jìn)程;
    preprintpanics(gp._panic)
    fatalpanic(gp._panic) // should not return
    *(*int)(nil) = 0      // not reached
}

上面邏輯可以拆分為循環(huán)內(nèi)和循環(huán)外兩部分去理解:

  • 循環(huán)內(nèi):程序執(zhí)行 defer,是否恢復(fù)正常的指令執(zhí)行,一切都在循環(huán)內(nèi)決定;
  • 循環(huán)外:一旦走到循環(huán)外,說明 _panic 沒人處理,認(rèn)命吧,程序即將退出;

for 循環(huán)內(nèi)

循環(huán)內(nèi)做的事情可以拆解成:

  • 遍歷 goroutine 的 defer 鏈表,獲取到一個 _defer 延遲函數(shù);
  • 獲取到 _defer 延遲函數(shù),設(shè)置標(biāo)識 d.started,綁定當(dāng)前 d._panic(用以在遞歸的時候判斷);
  • 執(zhí)行 _defer 延遲函數(shù);
  • 摘掉執(zhí)行完的 _defer 函數(shù);
  • 判斷 _panic.recovered 是否設(shè)置為 true,進(jìn)行相應(yīng)操作;

    如果是 true 那么重置 pc,sp 寄存器(一般從 deferreturn 指令前開始執(zhí)行),goroutine 投遞到調(diào)度隊列,等待執(zhí)行;

  • 重復(fù)以上步驟;

那些思考問題

你會發(fā)現(xiàn),更改 recovered 這個字段的時機(jī)只有在第三個步驟的時候。在任何地方,你都改不到 _panic.recovered 的值。

問題一:為什么 recover 一定要放在 defer 里面才生效?

因為,這是唯一的時機(jī) !

舉幾個淺顯對比的例子:

func main() {
    panic("test")
    recover()
}

上面的例子調(diào)用了 recover( ) 為什么還是 panic ?

因為根本執(zhí)行不到 recover 函數(shù),執(zhí)行順序是:

    panic 
        gopanic
            執(zhí)行 defer 鏈表 
            exit

有童鞋較真,那我把 recover() 放 panic("test") 前面唄?

func main() {
    recover()
    panic("test")
}

不行,因為執(zhí)行 recover 的時候,還沒有 _panic 掛在 goroutine 上面呢,recover 了個寂寞。

問題二:為什么 recover 已經(jīng)放在 defer 里面,但是進(jìn)程還是沒有恢復(fù)?

回憶一下上面 for 循環(huán)的操作:

    // 步驟:遍歷 _defer 鏈表
    d := gp._defer
    // 步驟:執(zhí)行 defer 函數(shù)
    reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
    // 步驟:執(zhí)行完成,把這個 defer 從鏈表里摘掉;
    gp._defer = d.link

劃重點(diǎn):在 gopanic 里,只遍歷執(zhí)行當(dāng)前 goroutine 上的 _defer 函數(shù)鏈條。所以,如果掛在其他 goroutine 的 defer 函數(shù)做了 recover ,那么沒有絲毫用途。

舉一個例子:

func main() { // g1
    go func() { // g2
        defer func() {
            recover()
        }()
    }()
    panic("test")
}

因為,panic 和 recover 在兩個不同的 goroutine,_panic 是掛在 g1 上的,recover 是在 g2 的 _defer 鏈條里。

gopanic 遍歷的是 g1 的 _defer 函數(shù)鏈表,跟 g2 八桿子打不著,g2 的 recover 自然拿不到 g1 的 _panic 結(jié)構(gòu),自然也不能設(shè)置 recovered 為 true ,所以程序還是崩了。

問題三:為什么 panic 之后,還能再 panic ?有啥影響?

這個其實(shí)很容易理解,有些童鞋可能想復(fù)雜了。gopanic 只是一個函數(shù)調(diào)用而已,那函數(shù)調(diào)用為啥不能嵌套遞歸?

當(dāng)然可以。

觸發(fā)的場景一般是:

  • gopanic 函數(shù)調(diào)用 _defer 延遲函數(shù);
  • defer 延遲函數(shù)里面又調(diào)用了 panic/gopanic 函數(shù);

這不就有了嘛,就是個簡單的函數(shù)嵌套而已,沒啥不可以的,并且在這種場景下,_panic 結(jié)構(gòu)體就會從 gp._panic 開始形成了一個鏈表。

而 gopanic 函數(shù)指令執(zhí)行的特殊在于兩點(diǎn):

  • _panic 被人設(shè)置成 recovered 之后,重置 pc,sp 寄存器,直接跨越 gopanic (還有嵌套的函數(shù)棧),跳轉(zhuǎn)到正常業(yè)務(wù)流程中;
  • 循環(huán)之外,等到最后,沒人處理 _panic 數(shù)據(jù),那就 exit 退出進(jìn)程,終止后續(xù)所有指令的執(zhí)行;

舉個嵌套的例子:

func main() {
    defer func() { // 延遲函數(shù)
        panic("panic again")
    }()
    panic("first")
}

函數(shù)執(zhí)行:

    gopanic
        defer 延遲函數(shù) 
            gopanic
                無 defer 延遲函數(shù)(遞歸往上),終止條件達(dá)成
     // 打印堆棧,退出程序
    fatalpanic

再看一個栗子:

func main() {
    println("=== begin ===")
    defer func() { // defer_0
        println("=== come in defer_0 ===")
    }()
    defer func() { // defer_1
        recover()
    }()
    defer func() { // defer_2
        panic("panic 2")
    }()
    panic("panic 1")
    println("=== end ===")
}

上面的函數(shù)會出打印堆棧退出進(jìn)程嗎?

答案是:不會。 猜一下輸出是啥?終端輸出結(jié)果如下:

?  panic ./test_panic
=== begin ===
=== come in defer_0 ===

你猜對了嗎?奇伢給你梳理了一下完整的路線:

main
    gopanic // 第一次
        1. 取出 defer_2,設(shè)置 started
        2. 執(zhí)行 defer_2 
            gopanic // 第二次
                1. 取出 defer_2,panic 設(shè)置成 aborted
                2. 把 defer_2 從鏈表中摘掉
                3. 執(zhí)行 defer_1
                    - 執(zhí)行 recover
                4. 摘掉 defer_1
                5. 執(zhí)行 recovery ,重置 pc 寄存器,跳轉(zhuǎn)到 defer_1 注冊時候,攜帶的指令,一般是跳轉(zhuǎn)到 deferreturn 上面幾個指令

    // 跳出 gopanic 的遞歸嵌套,直接到執(zhí)行 deferreturn 的地方;
    defereturn
        1. 執(zhí)行 defer 函數(shù)鏈,鏈條上還剩一個 defer_0,取出 defer_0;
        2. 執(zhí)行 defer_0 函數(shù)
    // main 函數(shù)結(jié)束

再來一個對比的例子:

func main() {
    println("=== begin ===")
    defer func() { // defer_0
        println("=== come in defer_0 ===")
    }()
    defer func() { // defer_1
        panic("panic 2")    
    }()
    defer func() { // defer_2
        recover()
    }()
    panic("panic 1")
    println("=== end ===")
}

上面的函數(shù)會打印堆棧,并且退出嗎?

**答案是:會。**輸出如下:

?  panic ./test_panic
=== begin ===
=== come in defer_0 ===
panic: panic 2

goroutine 1 [running]:
main.main.func2()
 /Users/code/gopher/src/panic/test_panic.go:9 +0x39
main.main()
 /Users/code/gopher/src/panic/test_panic.go:11 +0xf7

執(zhí)行路徑如下:

main
    gopanic // 第一次
        1. 取出 defer_2,設(shè)置 started
        2. 執(zhí)行 defer_2 
            - 執(zhí)行 recover,panic_1 字段被設(shè)置 recovered
        3. 把 defer_2 從鏈表中摘掉
        4. 執(zhí)行 recovery ,重置 pc 寄存器,跳轉(zhuǎn)到 defer_1 注冊時候,攜帶的指令,一般是跳轉(zhuǎn)到 deferreturn 上面幾個指令

    // 跳出 gopanic 的遞歸嵌套,執(zhí)行到 deferreturn 的地方;
    defereturn

        1. 遍歷 defer 函數(shù)鏈,取出 defer_1   
        2. 摘掉 defer_1
        2. 執(zhí)行 defer_1
            gopanic // 第二次
                1. defer 鏈表上有個 defer_0,取出來;
                2. 執(zhí)行 defer_0 (defer_0 沒有做 recover,只打印了一行輸出)
                3. 摘掉 defer_0,鏈表為空,跳出 for 循環(huán)
                3. 執(zhí)行 fatalpanic
                    - exit(2) 退出進(jìn)程
 

你猜對了嗎?

recovery 函數(shù)

最后,看一下關(guān)鍵的 recovery 函數(shù)。在 gopanic 函數(shù)中,在循環(huán)執(zhí)行 defer 函數(shù)的時候,如果發(fā)現(xiàn) _panic.recovered 字段被設(shè)置成 true 的時候,調(diào)用 mcall(recovery) 來執(zhí)行所謂的恢復(fù)。

看一眼 recovery 函數(shù)的實(shí)現(xiàn),這個函數(shù)極其簡單,就是恢復(fù) pc,sp 寄存器,重新把 Goroutine 投遞到調(diào)度隊列中。

// runtime/panic.go
func recovery(gp *g) {
    // 取出棧寄存器和程序計數(shù)器的值
    sp := gp.sigcode0
    pc := gp.sigcode1
    // 重置 goroutine 的 pc,sp 寄存器;
    gp.sched.sp = sp
    gp.sched.pc = pc
    // 重新投入調(diào)度隊列
    gogo(&gp.sched)
}

重置了 pc,sp 寄存器代表什么意思?

pc 寄存器指向指令所在的地址,換句話說,就是跳轉(zhuǎn)到其他地方執(zhí)行指令去了。而不是順序執(zhí)行 gopanic 后面的指令了,補(bǔ)回來了。

_defer.pc 的指令行執(zhí)行代碼,這個指令是哪里?

這個要回憶一下 defer 的章節(jié),defer 注冊延遲函數(shù)的時候?qū)?yīng)一個 _defer 結(jié)構(gòu)體,在 new 這個結(jié)構(gòu)體的時候,_defer.pc 字段賦值的就是 new 函數(shù)的下一行指令。這個在 深入剖析 defer 篇 詳細(xì)說過。

舉個例子,如果是棧上分配的話,那么在 deferprocStack ,所以,mcall(recovery) 跳轉(zhuǎn)到這個位置,其實(shí)后續(xù)就走 deferreturn 的邏輯了,執(zhí)行后續(xù)的 _defer 函數(shù)鏈。

本次 panic 就到此為止,相當(dāng)于就恢復(fù)了程序的正常運(yùn)行。

當(dāng)然,如果后續(xù)在 defer 函數(shù)里面又出現(xiàn) panic ,那可能形成一個 _panic 的鏈條,但是每一個的處理還是一樣的。

劃重點(diǎn):函數(shù)的 call,ret 是最常見的指令跳轉(zhuǎn)。最本源的就是 pc 寄存器,函數(shù)壓棧,出棧的時候,修改的也是 pc 寄存器,在 recovery 流程里,則來的更直接一點(diǎn),直接改 pc ,sp。

for 循環(huán)外

走到 for 循環(huán)外,那程序 100% 要退出了。因為 fatalpanic 里面打印一些堆棧信息之后,直接調(diào)用 exit 退出進(jìn)程的。到這已經(jīng)沒有任何機(jī)會了,只能乖乖就義。

退出的調(diào)用就在 fatalpanic 里:

func fatalpanic(msgs *_panic) {
    // 1. 打印協(xié)程堆棧

    // 2. 退出進(jìn)程
    systemstack(func() {
        exit(2)
    })

    *(*int)(nil) = 0 // not reached
}

所以這個問題清楚了嘛:為什么 panic 會讓 Go 進(jìn)程退出的 ?

因為調(diào)用了 exit(2) 嘛。

總結(jié)

  • panic() 會退出進(jìn)程,是因為調(diào)用了 exit 的系統(tǒng)調(diào)用;
  • recover() 并不是說只能在 defer 里面調(diào)用,而是只能在 defer 函數(shù)中才能生效,只有在 defer 函數(shù)里面,才有可能遇到 _panic 結(jié)構(gòu);
  • recover() 所在的 defer 函數(shù)必須和 panic 都是掛在同一個 goroutine 上,不能跨協(xié)程,因為 gopanic 只會執(zhí)行當(dāng)前 goroutine 的延遲函數(shù);
  • 所謂 panic 的恢復(fù),就是重置 pc 寄存器,直接跳轉(zhuǎn)程序執(zhí)行的指令,跳轉(zhuǎn)到原本 defer 函數(shù)執(zhí)行完該跳轉(zhuǎn)的位置(deferreturn 執(zhí)行),從 gopanic 函數(shù)中跳出,不再回來,自然就不會再 fatalpanic ;
  • panic 為啥能嵌套?這個問題就像是在問為什么函數(shù)調(diào)用可以嵌套一樣,因為這個本質(zhì)是一樣的。

后記

panic 就是一個特殊的函數(shù)調(diào)用,沒啥特殊的。只所以特殊,是因為有一些特殊的指令跳轉(zhuǎn)而已。

以上就是Go 的panic和recover函數(shù)使用細(xì)節(jié)深入探究的詳細(xì)內(nèi)容,更多關(guān)于Go panic recover函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go語言寫的簡要數(shù)據(jù)同步工具詳解

    go語言寫的簡要數(shù)據(jù)同步工具詳解

    作為go-etl工具的作者,想要安利一下這個小巧的數(shù)據(jù)同步工具,它在同步百萬級別的數(shù)據(jù)時表現(xiàn)極為優(yōu)異,基本能在幾分鐘完成數(shù)據(jù)同步,這篇文章主要介紹了go語言寫的簡要數(shù)據(jù)同步工具,需要的朋友可以參考下
    2024-07-07
  • 對Golang中的runtime.Caller使用說明

    對Golang中的runtime.Caller使用說明

    這篇文章主要介紹了對Golang中的runtime.Caller使用說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go?web中cookie值安全securecookie庫使用原理

    Go?web中cookie值安全securecookie庫使用原理

    這篇文章主要為大家介紹了Go?web中cookie值安全securecookie庫使用及實(shí)現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • golang正則之命名分組方式

    golang正則之命名分組方式

    這篇文章主要介紹了golang正則之命名分組方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang?int函數(shù)使用實(shí)例全面教程

    Golang?int函數(shù)使用實(shí)例全面教程

    這篇文章主要為大家介紹了Golang?int函數(shù)使用實(shí)例全面教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Golang使用Zookeeper實(shí)現(xiàn)分布式鎖

    Golang使用Zookeeper實(shí)現(xiàn)分布式鎖

    分布式鎖是一種在分布式系統(tǒng)中用于控制并發(fā)訪問的機(jī)制,ZooKeeper?和?Redis?都是常用的實(shí)現(xiàn)分布式鎖的工具,本文就來使用Zookeeper實(shí)現(xiàn)分布式鎖,希望對大家有所幫助
    2024-02-02
  • GO實(shí)現(xiàn)跳躍表的示例詳解

    GO實(shí)現(xiàn)跳躍表的示例詳解

    跳表全稱叫做跳躍表,簡稱跳表,是一個隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),實(shí)質(zhì)就是一種可以進(jìn)行二分查找的有序鏈表。本文將利用GO語言編寫一個跳表,需要的可以參考一下
    2022-12-12
  • 使用go實(shí)現(xiàn)一個超級mini的消息隊列的示例代碼

    使用go實(shí)現(xiàn)一個超級mini的消息隊列的示例代碼

    本文主要介紹了使用go實(shí)現(xiàn)一個超級mini的消息隊列的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • go語言接口之接口值舉例詳解

    go語言接口之接口值舉例詳解

    接口是一種抽象類型,是對其他類型行為的概括與抽象,從語法角度來看,接口是一組方法定義的集合,下面這篇文章主要給大家介紹了關(guān)于go語言接口之接口值的相關(guān)資料,文章通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • Go存儲基礎(chǔ)使用direct io方法實(shí)例

    Go存儲基礎(chǔ)使用direct io方法實(shí)例

    這篇文章主要介紹了Go存儲基礎(chǔ)之如何使用direct io方法實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12

最新評論