1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      Go中defer和panic以及recover的示例分析

      這篇文章將為大家詳細(xì)講解有關(guān)Go中defer和panic以及recover的示例分析,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

      創(chuàng)新互聯(lián)建站是一家集網(wǎng)站建設(shè),淮陽(yáng)企業(yè)網(wǎng)站建設(shè),淮陽(yáng)品牌網(wǎng)站建設(shè),網(wǎng)站定制,淮陽(yáng)網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,淮陽(yáng)網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

      defer

      defer 語(yǔ)句將一個(gè)函數(shù)放入一個(gè)棧中,defer 會(huì)在當(dāng)前函數(shù)返回前執(zhí)行傳入的函數(shù),經(jīng)常用于關(guān)閉文件描述符,數(shù)據(jù)庫(kù)連接,redis連接等,用于清理資源,避免資源浪費(fèi)。比如下面這個(gè)栗子

      package main
      
      import (
       "fmt" "goapp/src/math")
      
      func main() {
      
       sum:=math.Add(2,3)
       fmt.Println(sum)
       defer func() {fmt.Println("i am defer1")}()
       res := test_defer();
       fmt.Println(res)
      
      }
      
      func test_defer() float64  {
       defer func() {fmt.Println("i am defer2")}()
       defer func() {fmt.Println("i am defer3")}()
       res :=math3.Mod(5,3)
       return res;
      }

      執(zhí)行結(jié)果是什么呢?

      1. 執(zhí)行 一個(gè)加法,打印返回值 5;

      2.defer1入棧

      3.執(zhí)行函數(shù)test_defer,defer2入棧,defer3入棧,執(zhí)行函數(shù)邏輯,在return 之前 呢,會(huì)執(zhí)行 棧里面的defer,棧嘛,先進(jìn)后出,和隊(duì)列相反,所以依次執(zhí)行defer3,defer2,然后返回結(jié)果

      4.main函數(shù)收到test_defer的返回值,開始打印結(jié)果

      5.main函數(shù)在結(jié)束之前呢,會(huì)執(zhí)行一下本函數(shù)內(nèi)的defer,所以開始執(zhí)行defer1

      那結(jié)果是不是這樣執(zhí)行的呢?我們來(lái)看一下結(jié)果,毫無(wú)相差

      Go中defer和panic以及recover的示例分析

      image.png

      那么此處可能有小伙伴要問一下,defer為什么要設(shè)計(jì)成棧?

      • 通俗來(lái)講一個(gè)場(chǎng)景,defer 是做清場(chǎng)工作的,對(duì)吧,那么這樣一個(gè)場(chǎng)景,一個(gè)小偷去倉(cāng)庫(kù)偷東西,干完活了,要擦除腳印對(duì)吧,那他不可能從進(jìn)門的位置開始擦腳印吧,他只能退著擦,先擦最后一步的腳印,而且,很多時(shí)候最后一步是基于前面的基礎(chǔ)上的,比如,還是這個(gè)小偷,他想偷柜子里面的珠寶,那他是不是得先打開門啊,那小偷做清理工作的時(shí)候,不可能先關(guān)閉門,在關(guān)閉柜子吧。

      • defer是用來(lái)釋放資源的,比如一個(gè)操作,先給文件上鎖,然后修改文件。那defer執(zhí)行的時(shí)候應(yīng)該是先關(guān)閉文件,再釋放鎖。如果釋放鎖,再關(guān)閉文件,那不是亂套了嗎?從因果關(guān)系上說(shuō),后分配的資源可能會(huì)依賴前面的資源,但是前面的資源肯定不會(huì)依賴后面打開的資源。所以倒過來(lái)執(zhí)行關(guān)閉 不會(huì)產(chǎn)生問題。

      那有的人說(shuō),我就是讓defer先進(jìn)先出,不行嗎?允許是允許,但是不提倡。哈哈,是不是感受到了羅翔老師的氣場(chǎng),請(qǐng)看下面的代碼,如果defer嵌套,那么defer會(huì)從外往里執(zhí)行,剝洋蔥似的,一層一層剝。

      package main
      
      func main() {
       defer func() {
        println("i am defer1")
        defer func() {
         println("i am defer2")
         defer func() {
          println("i am defer3")
         }()
        }()
       }()
      
       panic("i am panic")
      }

      panic

      panic往往和recover是成對(duì)出現(xiàn)的,與defer也有緊密的聯(lián)系,panic能夠改變程序的控制流,調(diào)用panic后會(huì)立刻停止執(zhí)行當(dāng)前函數(shù)的剩余代碼,并在當(dāng)前Goroutine(協(xié)程)中遞歸執(zhí)行調(diào)用方的defer

      我們看下面一段代碼

      package main
      
      import "time"func main()  {
       defer func() {
        println("i am main defer1")
       }()
      
       go func() {
        defer func() {
         println("i am goroutine defer2")
        }()
      
        defer func() {
         println("i am goroutine defer3")
        }()
      
        panic("i am panic")
      
        defer func() {
         println("i am goroutine defer4")
        }()
       }()
       time.Sleep(1 * time.Second)
      }
      • 從前面的分析我們得知以下結(jié)果

      1. defer1 入棧

      2.執(zhí)行g(shù)oroutine 3.defer2 入棧 4.defer3入棧 5.panic打斷程序執(zhí)行,依次執(zhí)行defer3,defer2,panic,而panic 后面的程序不會(huì)再運(yùn)行,并且main里面的defer也不會(huì)執(zhí)行

      Go中defer和panic以及recover的示例分析

      image.png

      • 我為什么要加time.Sleep 如果不加呢?

      從截圖里面看到,如果沒有time.Sleep,協(xié)程好像沒有被執(zhí)行一樣,為什么會(huì)這樣呢?因?yàn)槲覀冎溃瑓f(xié)程不是搶占式的,如果刪除time.Sleep,主goroutine不會(huì)放棄對(duì)輔助goroutine的控制,但是goroutine 必須放棄控制才能運(yùn)行另一個(gè)goroutine,而time.Sleep就是放棄控制的一種方法。簡(jiǎn)單來(lái)說(shuō),你這個(gè)程序 從頭到尾都是被main 主協(xié)程占用著,子協(xié)程不會(huì)主動(dòng)搶占cpu,那么必須得是主協(xié)程主動(dòng)讓出cpu,讓子協(xié)程有機(jī)會(huì)被cpu輪詢到,子協(xié)程才會(huì)被執(zhí)行

      順道說(shuō)一下什么是協(xié)程
      • 協(xié)程是go語(yǔ)言最重要的特色之一,那么我們?cè)趺蠢斫鈪f(xié)程呢?協(xié)程,簡(jiǎn)單說(shuō)就是輕量級(jí)的線程,一個(gè)協(xié)程的大小是2k 左右,這也解釋了為什么go能單機(jī)百萬(wàn)。

      • go語(yǔ)言里的協(xié)程創(chuàng)建很簡(jiǎn)單,在go關(guān)鍵詞后面加一個(gè)函數(shù)調(diào)用就可以了。代碼舉栗

      package main
      
      import "time"func main()  {
       println("i am main goroutine")
       go func() {
        println("i am goroutine_in_1")
        go func() {
         println("i am goroutine_in_2")
         go func() {
          println("i am goroutine_in_3")
         }()
        }()
       }()
      
       time.Sleep(1*time.Second);
       println("main goroutine is over")
      }

      Go中defer和panic以及recover的示例分析

      image.png

      main 函數(shù)是怎么運(yùn)行的呢?其實(shí)main函數(shù)也是運(yùn)行在goroutine里面,不過是主協(xié)程,上面的栗子我們是嵌套了幾個(gè)協(xié)程,但是他們中間并沒有什么層級(jí)關(guān)系,協(xié)程只有兩種,子協(xié)程和主協(xié)程。上面的代碼中,我們讓主協(xié)程休息了一秒,等待子協(xié)程返回結(jié)果。如果不讓主協(xié)程休息一秒,即讓出cpu,讓子協(xié)程是沒有機(jī)會(huì)執(zhí)行的,因?yàn)橹鲄f(xié)程運(yùn)行結(jié)束后,不管子協(xié)程是任何狀態(tài),都會(huì)全部消亡。

      但是在實(shí)際使用中,我們要保護(hù)好每一個(gè)子協(xié)程,確保他們安全運(yùn)行,一個(gè)子協(xié)程的異常會(huì)傳播到主協(xié)程,直接導(dǎo)致主協(xié)程掛掉,程序崩潰。比如下面這個(gè)栗子

      package main
      
      import "time"func main()  {
       println("i am main goroutine")
       go func() {
        println("i am goroutine_in_1")
        go func() {
         println("i am goroutine_in_2")
         go func() {
          println("i am goroutine_in_3")
          panic("i am panic")
         }()
        }()
       }()
      
       time.Sleep(1*time.Second);
       println("main goroutine is over")
      }

      最后一句,main goroutine is over沒有打印,程序沒有執(zhí)行到。 前面我們說(shuō)到了。不管你是什么樣的程序,遇到panic 我就終止程序往下執(zhí)行,哪怕是子協(xié)程呢!好了,協(xié)程先說(shuō)到這里。我們繼續(xù)往下看recover

      recover

      recover 可以中止panic造成的程序崩潰,它是一個(gè)只能在defer中發(fā)揮作用的函數(shù)。在其他作用域中不會(huì)發(fā)揮作用。為什么這么說(shuō)呢?我們看下面這個(gè)栗子

      package main
      
      import "fmt"func main() {
       defer func() {
        println("i am main")
       }()
       if err := recover();err != nil {
        fmt.Println(err)
       }
       panic("i am panic")
      }

      看一下執(zhí)行結(jié)果

      Go中defer和panic以及recover的示例分析

      image.png

      我們看到,遇到panic,執(zhí)行了defer,然后執(zhí)行了panic ,并沒有執(zhí)行if條件判斷,為什么?recover是捕捉錯(cuò)誤的,運(yùn)行到if 還沒有錯(cuò)誤,捕捉什么?運(yùn)行到panic 的時(shí)候 if 已經(jīng)執(zhí)行過了,怎么捕捉?那么可能有人想,我把if放到panic后面不就行了嗎?行嗎?答案是否定的,panic 我們前面已經(jīng)說(shuō)過了,甭管你是誰(shuí),看見我就得停止。那就回到我們剛才說(shuō)的,panic 出現(xiàn),程序停止往下執(zhí)行,但是程序會(huì)循環(huán)執(zhí)行defer啊,那如果我在defer里面捕捉錯(cuò)誤,是不是就可以解決這個(gè)問題了呢。可見go的設(shè)計(jì)者是用心良苦!到這里有沒有人會(huì)問一個(gè)問題defer可以嵌套,那么panic能否嵌套呢?當(dāng)然可以,defer可以容納一切,panic放到defer里面一樣可以嵌套

      package main
      
      func main() {
       defer func() {
        defer func() {
         defer func() {
          panic("i am panic3")
         }()
         panic("i am panic2")
        }()
        panic("i am panic1")
       }()
      
       panic("i am panic")
      }

      為什么會(huì)先執(zhí)行 最后一行panic ,才執(zhí)行defer呢,這和前面說(shuō)的遇到panic先執(zhí)行defer有點(diǎn)出入是吧,但是你這樣看 defer優(yōu)先于panic優(yōu)先于defer+panic。

      那么現(xiàn)在,我們來(lái)寫一個(gè)例子,看defer 如何捕捉panic并恢復(fù)程序正常執(zhí)行

      package main
      
      import "fmt"func main() {
       havePanic();
       println("i will go on ")
      }
      
      func havePanic()  {
       defer func() {
        if err:=recover();err !=nil {
         fmt.Println("i get a panic")
         fmt.Println(err)
        }
       }()
       panic("i am panic");
      }

      解讀一下上面的程序,執(zhí)行havePanic ,havePanic的第一個(gè)defer入棧,往下執(zhí)行碰到panic,首先會(huì)執(zhí)行defer,defer里面打印了err信息,并可以做一些其他的處理,比如記錄日志,重試什么的。然后main繼續(xù)執(zhí)行下面的print,看一下執(zhí)行結(jié)果

      Go中defer和panic以及recover的示例分析

      image.png

      下面再補(bǔ)充一點(diǎn)協(xié)程的知識(shí)

      go不是號(hào)稱百萬(wàn)協(xié)程嗎?那么我們真給它來(lái)個(gè)百萬(wàn)協(xié)程看一下我的電腦到底能不能hold住

      來(lái)!寫一段代碼

      package main
      
      import (
       "fmt" "time")
      
      func main()  {
       i :=1;
       for  {
        go func() {
         for  {
          time.Sleep(time.Second)
         }
        }()
        if i > 1000000 {
         fmt.Printf("我已經(jīng)啟動(dòng)了%d個(gè)協(xié)程\n",i)
        }else{
         fmt.Printf("當(dāng)前是第%d個(gè)協(xié)程\n",i)
        }
        i++
       }
      
      }

      截圖看一下我當(dāng)前的機(jī)器狀態(tài)

      百萬(wàn)協(xié)程掛起之后的截圖

      Go中defer和panic以及recover的示例分析

      image.png

      因?yàn)檩敵龈簧纤俣绕鋵?shí)最后跑了1842504個(gè)協(xié)程

      說(shuō)一下跑后感:風(fēng)扇呼呼的轉(zhuǎn)了大概三分鐘的樣子 ,我算了一下一個(gè)協(xié)程大概是2.45kb的樣子

      Go中defer和panic以及recover的示例分析

      image.png

      協(xié)程和線程的區(qū)別

      一個(gè)進(jìn)程內(nèi)部可以運(yùn)行多個(gè)線程,而每個(gè)線程又可以運(yùn)行很多協(xié)程。線程要負(fù)責(zé)對(duì)協(xié)程進(jìn)行調(diào)度,保證每個(gè)協(xié)程都有機(jī)會(huì)得到執(zhí)行。當(dāng)一個(gè)協(xié)程睡眠時(shí),它要將線程的運(yùn)行權(quán)讓給其它的協(xié)程來(lái)運(yùn)行,而不能持續(xù)霸占這個(gè)線程。同一個(gè)線程內(nèi)部最多只會(huì)有一個(gè)協(xié)程正在運(yùn)行。

      線程的調(diào)度是由操作系統(tǒng)負(fù)責(zé)的,調(diào)度算法運(yùn)行在內(nèi)核態(tài),而協(xié)程的調(diào)用是由 Go 語(yǔ)言的運(yùn)行時(shí)負(fù)責(zé)的,調(diào)度算法運(yùn)行在用戶態(tài)。

      協(xié)程可以簡(jiǎn)化為三個(gè)狀態(tài),運(yùn)行態(tài)、就緒態(tài)和休眠態(tài)。同一個(gè)線程中最多只會(huì)存在一個(gè)處于運(yùn)行態(tài)的協(xié)程,就緒態(tài)的協(xié)程是指那些具備了運(yùn)行能力但是還沒有得到運(yùn)行機(jī)會(huì)的協(xié)程,它們隨時(shí)會(huì)被調(diào)度到運(yùn)行態(tài),休眠態(tài)的協(xié)程還不具備運(yùn)行能力,它們是在等待某些條件的發(fā)生,比如 IO 操作的完成、睡眠時(shí)間的結(jié)束等。

      操作系統(tǒng)對(duì)線程的調(diào)度是搶占式的,也就是說(shuō)單個(gè)線程的死循環(huán)不會(huì)影響其它線程的執(zhí)行,每個(gè)線程的連續(xù)運(yùn)行受到時(shí)間片的限制。

      Go 語(yǔ)言運(yùn)行時(shí)對(duì)協(xié)程的調(diào)度并不是搶占式的。如果單個(gè)協(xié)程通過死循環(huán)霸占了線程的執(zhí)行權(quán),那這個(gè)線程就沒有機(jī)會(huì)去運(yùn)行其它協(xié)程了,你可以說(shuō)這個(gè)線程假死了。不過一個(gè)進(jìn)程內(nèi)部往往有多個(gè)線程,假死了一個(gè)線程沒事,全部假死了才會(huì)導(dǎo)致整個(gè)進(jìn)程卡死。

      每個(gè)線程都會(huì)包含多個(gè)就緒態(tài)的協(xié)程形成了一個(gè)就緒隊(duì)列,如果這個(gè)線程因?yàn)槟硞€(gè)別協(xié)程死循環(huán)導(dǎo)致假死,那這個(gè)隊(duì)列上所有的就緒態(tài)協(xié)程是不是就沒有機(jī)會(huì)得到運(yùn)行了呢?Go 語(yǔ)言運(yùn)行時(shí)調(diào)度器采用了 work-stealing 算法,當(dāng)某個(gè)線程空閑時(shí),也就是該線程上所有的協(xié)程都在休眠(或者一個(gè)協(xié)程都沒有),它就會(huì)去其它線程的就緒隊(duì)列上去偷一些協(xié)程來(lái)運(yùn)行。也就是說(shuō)這些線程會(huì)主動(dòng)找活干,在正常情況下,運(yùn)行時(shí)會(huì)盡量平均分配工作任務(wù)。

      我的線程數(shù)到底有多少?

      默認(rèn)情況下,Go 運(yùn)行時(shí)會(huì)將線程數(shù)會(huì)被設(shè)置為機(jī)器 CPU 邏輯核心數(shù)。同時(shí)它內(nèi)置的 runtime 包提供了 GOMAXPROCS(n int) 函數(shù)允許我們動(dòng)態(tài)調(diào)整線程數(shù),注意這個(gè)函數(shù)名字是全大寫。該函數(shù)會(huì)返回修改前的線程數(shù),如果參數(shù) n <=0 ,就不會(huì)產(chǎn)生修改效果,等價(jià)于讀操作。

      package main
      
      import (
       "fmt" "runtime")
      
      func main()  {
       fmt.Print(runtime.GOMAXPROCS(0))//獲取默認(rèn)線程數(shù) 8
       println("\n")
       runtime.GOMAXPROCS(10)//設(shè)置線程數(shù)為10
       fmt.Print(runtime.GOMAXPROCS(0))//獲取新線程數(shù) 10
      }

      關(guān)于Go中defer和panic以及recover的示例分析就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。


      名稱欄目:Go中defer和panic以及recover的示例分析
      文章源于:http://www.ef60e0e.cn/article/gssdjj.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        四平市| 高平市| 蓬莱市| 保定市| 河间市| 岢岚县| 绥滨县| 贵州省| 文安县| 盈江县| 邓州市| 龙井市| 无锡市| 红河县| 武鸣县| 江安县| 三都| 宝坻区| 荣昌县| 出国| 乌兰察布市| 商城县| 奉节县| 博白县| 德庆县| 余干县| 湖口县| 苏尼特右旗| 五家渠市| 德兴市| 南昌县| 星座| 珲春市| 阳泉市| 崇仁县| 安新县| 南和县| 津市市| 肃北| 莒南县| 秀山|