1. goroutine配上panic會怎樣?

        共 2240字,需瀏覽 5分鐘

         ·

        2022-02-20 10:21

        這里是Z哥的個人公眾號

        每周五11:45 按時送達

        當(dāng)然了,也會時不時加個餐~

        我的第「220」篇原創(chuàng)敬上


        大家好,我是Z哥。


        最近用 Golang 進行編碼也有3個月了,說來慚愧,到現(xiàn)在還沒正兒八經(jīng)深入學(xué)習(xí)一下 Golang,一直被工作趕著往前在跑。


        最近正好在工作中遇到一個問題,需要對 Golang 中的 goroutine 和 panic & recover 稍做深入的了解,算是忙里偷閑學(xué)習(xí)一下。



        對 goroutine 的底層細節(jié)就不展開了,網(wǎng)上有不少相關(guān)的文章解讀,如果你愿意的話,也可以去扒一下 Golang 的源碼。


        簡單對 goroutine 進行一下概括就是:


        goroutine 實現(xiàn)了 M:N 的線程模型,是協(xié)程的一種實現(xiàn)。golang 內(nèi)置的調(diào)度器,可以讓多核 CPU 中每個 CPU 執(zhí)行一個協(xié)程。


        單從表現(xiàn)來看,你可以將 goroutine 看作是 java 之類編程語言中的多線程的運行效果。


        好了,那么問題來了:goroutine 中發(fā)生 panic 會怎樣?


        話不多說,實踐是檢驗真理的唯一標(biāo)準,我們直接上手 coding。


        func main()? {

        go panicInGoroutine()


        //以下3行代碼是為了讓控制臺掛起,等待gorouine運行完畢。

        fmt.Println("wait")

        input := bufio.NewScanner(os.Stdin)

        input.Scan()

        }


        func panicInGoroutine()? {

        panic("panic in goroutine.")

        }


        運行代碼的結(jié)果如下:


        292e7915448138e96b96b4518cc10b69.webp


        可以看到,整個程序都崩了。


        那么,如果在 goroutine 里的 goroutine 發(fā)出 panic 呢?也是一樣的效果,程序崩了。


        可能你會覺得整個程序之所以會崩,是因為異常被層層上拋到主線程導(dǎo)致的,其實并非如此。在 Golang 中,任何地方發(fā)生的任意一個 panic,都會直接程序退出。



        那么怎么才能讓程序不退出呢?


        通過調(diào)用 recover() 方法來捕獲 panic 并恢復(fù)將要崩掉的程序。


        func main() {

        go panicInGoroutine()


        //以下3行代碼是為了讓控制臺掛起,等待gorouine運行完畢。

        fmt.Println("wait")

        input := bufio.NewScanner(os.Stdin)

        input.Scan()

        }


        func panicInGoroutine() {

        //recover()必須要和defer配合一起用,確保一旦執(zhí)行到該方法體,這里定義的defer方法一定會被執(zhí)行,哪怕是發(fā)生了panic。

        defer func() {

        err := recover()

        if err != nil {

        fmt.Printf("recover receive a err: %+v \n", err)

        }

        }()


        panic("panic in goroutine.")

        }


        執(zhí)行上面的代碼,結(jié)果如下:


        330732b5a9c8d6735a1143bf2dc20dc2.webp


        可以看到,程序沒有再崩了。那么新的問題又來了,能不能把 recover() 放到最外層的方法里,這樣可以更好地實現(xiàn)一次 recover() 覆蓋當(dāng)前方法其下所有的 panic。


        func main() {

        ????????defer func() {

        err := recover()

        if err != nil {

        fmt.Printf("recover receive a err: %+v \n", err)

        }

        }()


        go panicInGoroutine()


        //以下3行代碼是為了讓控制臺掛起,等待gorouine運行完畢。

        fmt.Println("wait")

        input := bufio.NewScanner(os.Stdin)

        input.Scan()

        }


        func panicInGoroutine() {

        panic("panic in goroutine.")

        }


        運行之后的結(jié)果:


        889cc62032a2ff0237ad656774c76688.webp


        竟然還是崩了。如果你是一位 Java 或者 .Net 的程序員習(xí)慣了 try-catch-finally 的運行效果肯定對這個結(jié)果比較意外。在父方法定義的 recover() 竟然無法捕獲到子方法里的 panic。


        其實這里的原因是,外層方法中定義的 recover() 無法捕獲通過 goroutine 執(zhí)行的子方法中拋出的 panic。在上面的代碼中,我們把 go panicInGoroutine() 前面的 go 去掉就可以正常捕獲了。


        b89314cf70be228046cb91e11231c75f.webp


        好了,那么根據(jù)以上這些信息得到的處理 panic 的正確姿勢是什么呢?


        1. 必須通過 defer 關(guān)鍵字來調(diào)用 recover()。

        2. 當(dāng)通過?goroutine 調(diào)用某個方法,一定要確保內(nèi)部有 recover() 機制。


        如果你想進一步深入了解 panic 和 recove r的機制,分享你一個超棒的硬核視頻:https://www.bilibili.com/video/BV155411Y7XT,第一遍看可能會有點暈,建議反復(fù)看,直到完全理解其原理。



        推薦閱讀:


        原創(chuàng)不易,如果你覺得這篇文章還不錯,就「點贊」或者「在看」一下吧,鼓勵我的創(chuàng)作 :)

        也可以分享我的公眾號名片給有需要的朋友們。

        如果你有關(guān)于軟件架構(gòu)、分布式系統(tǒng)、產(chǎn)品、運營的困惑

        可以試試點擊「閱讀原文

        瀏覽 57
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 成年人在线视频观看 | 国产199精品 | xnxx国产精品 | 丰满少妇喷水大秀高清在线 | 91av视频在线 |