1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        回答我,停止 Goroutine 有幾種方法?

        共 421字,需瀏覽 1分鐘

         ·

        2021-10-28 22:53

        大家好,我是煎魚。

        協(xié)程(goroutine)作為 Go 語言的扛把子,經(jīng)常在各種 Go 工程項目中頻繁露面,甚至有人會為了用 goroutine 而強行用他。

        在 Go 工程師的面試中,也繞不開他,會有人問 ”如何停止一個 goroutine?”,一下子就把話題范圍擴大了,這是一個涉及多個知識點的話題,能進一步深入問。

        為此,今天煎魚就帶大家了解一下停止 goroutine 的方法!

        goroutine 案例

        在日常的工作中,我們常會有這樣的 Go 代碼,go 關(guān)鍵字一把搜起一個 goroutine:

        func?main()?{?
        ?ch?:=?make(chan?string,?6)
        ?go?func()?{
        ??for?{
        ???ch?<-?"腦子進煎魚了"
        ??}
        ?}()
        }

        初入 goroutine 大門的開發(fā)者可能就完事了,但跑一段時間后,他就可能會遇到一些問題,苦苦排查...

        像是:當(dāng) goroutine 內(nèi)的任務(wù),運行的太久,又或是卡死了...就會一直阻塞在系統(tǒng)中,變成 goroutine 泄露,或是間接造成資源暴漲,會帶來許多的問題。

        如何在停止 goroutine,就成了一門必修技能了,不懂就沒法用好 goroutine。

        關(guān)閉 channel

        第一種方法,就是借助 channel 的 close 機制來完成對 goroutine 的精確控制。

        代碼如下:

        func?main()?{
        ?ch?:=?make(chan?string,?6)
        ?go?func()?{
        ??for?{
        ???v,?ok?:=?<-ch
        ???if?!ok?{
        ????fmt.Println("結(jié)束")
        ????return
        ???}
        ???fmt.Println(v)
        ??}
        ?}()

        ?ch?<-?"煎魚還沒進鍋里..."
        ?ch?<-?"煎魚進腦子里了!"
        ?close(ch)
        ?time.Sleep(time.Second)
        }

        在 Go 語言的 channel 中,channel 接受數(shù)據(jù)有兩種方法:

        msg?:=?<-ch
        msg,?ok?:=?<-ch

        這兩種方式對應(yīng)著不同的 runtime 方法,我們可以利用其第二個參數(shù)進行判別,當(dāng)關(guān)閉 channel 時,就根據(jù)其返回結(jié)果跳出。

        另外我們也可以利用 for range 的特性:

        ?go?func()?{
        ??for?{
        ???for?v?:=?range?ch?{
        ????fmt.Println(v)
        ???}
        ??}
        ?}()

        其會一直循環(huán)遍歷通道 ch,直到其關(guān)閉為止,是頗為常見的一種用法。

        定期輪詢 channel

        第二種方法,是更為精細的方法,其結(jié)合了第一種方法和類似信號量的處理方式。

        代碼如下:

        func?main()?{
        ?ch?:=?make(chan?string,?6)
        ?done?:=?make(chan?struct{})
        ?go?func()?{
        ??for?{
        ???select?{
        ???case?ch?<-?"腦子進煎魚了":
        ???case?<-done:
        ????close(ch)
        ????return
        ???}
        ???time.Sleep(100?*?time.Millisecond)
        ??}
        ?}()

        ?go?func()?{
        ??time.Sleep(3?*?time.Second)
        ??done?<-?struct{}{}
        ?}()

        ?for?i?:=?range?ch?{
        ??fmt.Println("接收到的值:?",?i)
        ?}

        ?fmt.Println("結(jié)束")
        }

        在上述代碼中,我們聲明了變量 done,其類型為 channel,用于作為信號量處理 goroutine 的關(guān)閉。

        而 goroutine 的關(guān)閉是不知道什么時候發(fā)生的,因此在 Go 語言中會利用 for-loop 結(jié)合 select 關(guān)鍵字進行監(jiān)聽,再進行完畢相關(guān)的業(yè)務(wù)處理后,再調(diào)用 close 方法正式關(guān)閉 channel。

        若程序邏輯比較簡單結(jié)構(gòu)化,也可以不調(diào)用 close 方法,因為 goroutine 會自然結(jié)束,也就不需要手動關(guān)閉了。

        使用 context

        第三種方法,可以借助 Go 語言的上下文(context)來做 goroutine 的控制和關(guān)閉。

        代碼如下:

        func?main()?{
        ?ch?:=?make(chan?struct{})
        ?ctx,?cancel?:=?context.WithCancel(context.Background())

        ?go?func(ctx?context.Context)?{
        ??for?{
        ???select?{
        ???case?<-ctx.Done():
        ????ch?<-?struct{}{}
        ????return
        ???default:
        ????fmt.Println("煎魚還沒到鍋里...")
        ???}

        ???time.Sleep(500?*?time.Millisecond)
        ??}
        ?}(ctx)

        ?go?func()?{
        ??time.Sleep(3?*?time.Second)
        ??cancel()
        ?}()

        ?<-ch
        ?fmt.Println("結(jié)束")
        }

        在 context 中,我們可以借助 ctx.Done 獲取一個只讀的 channel,類型為結(jié)構(gòu)體。可用于識別當(dāng)前 channel 是否已經(jīng)被關(guān)閉,其原因可能是到期,也可能是被取消了。

        因此 context 對于跨 goroutine 控制有自己的靈活之處,可以調(diào)用 context.WithTimeout 來根據(jù)時間控制,也可以自己主動地調(diào)用 cancel 方法來手動關(guān)閉。

        干掉另外一個 goroutine

        在了解了停止 goroutine 的 3 種經(jīng)典方法后,又有小伙伴提出了新的想法。就是 “我想在 goroutineA 里去停止 goroutineB,有辦法嗎?

        答案是不能,因為在 Go 語言中,goroutine 只能自己主動退出,一般通過 channel 來控制,不能被外界的其他 goroutine 關(guān)閉或干掉,也沒有 goroutine 句柄的顯式概念。

        go/issues/32610

        在 Go issues 中也有人提過類似問題,Dave Cheney 給出了一些思考:

        • 如果一個 goroutine 被強行停止了,它所擁有的資源會發(fā)生什么?堆棧被解開了嗎?defer 是否被執(zhí)行?
          • 如果執(zhí)行 defer,該 goroutine 可能可以繼續(xù)無限期地生存下去。
          • 如果不執(zhí)行 defer,該 goroutine 原本的應(yīng)用程序系統(tǒng)設(shè)計邏輯將會被破壞,這肯定不合理。
        • 如果允許強制停止 goroutine,是要釋放所有東西,還是直接把它從調(diào)度器中踢出去,你想通過此解決什么問題?

        這都是值得深思的,另外一旦放開這種限制。作為程序員,你維護代碼。很有可能就不知道 goroutine 的句柄被傳到了哪里,又是在何時何地被人莫名其妙關(guān)閉,非常糟糕...

        總結(jié)

        在今天這篇文章中,我們介紹了在 Go 語言中停止 goroutine 的三大經(jīng)典方法(channel、context,channel+context)和其背后的使用原理。

        同時針對 goroutine 不可以跨 goroutine 強制停止的原因進行了分析。其實 goroutine 的設(shè)計就是這樣的,包括像 goroutine+panic+recover 的設(shè)計也是遵循這個原理,因此也有的 Go 開發(fā)者總是會誤以為跨 goroutine 能有 recover 接住...

        記住,在 Go 語言中每一個 goroutine 都需要自己承擔(dān)自己的任何責(zé)任,這是基本原則。

        (你已經(jīng)是個成熟的 goroutine 了...)

        關(guān)注煎魚,吸取他的知識???



        你好,我是煎魚。高一折騰過前端,參加過國賽拿了獎,大學(xué)搞過 PHP?,F(xiàn)在整 Go,在公司負責(zé)微服務(wù)架構(gòu)等相關(guān)工作推進和研發(fā)。

        從大學(xué)開始靠自己賺生活費和學(xué)費,到出版 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領(lǐng)域最有觀點專家)榮譽,點擊藍字查看我的出書之路。

        日常分享高質(zhì)量文章,輸出 Go 面試、工作經(jīng)驗、架構(gòu)設(shè)計,加微信拉讀者交流群,記得點贊!

        瀏覽 103
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲成人精品在线观看 | 欧美成人三级网站 | 免费 成人进口 | 欧美婷婷影院 | 久久999 | 口述野战做爰的细节描述和过程 | 巨爆乳幕巨爆乳熟女电影 | 操欧美美女 | 男男超h高h污肉1v1 | 波多野结衣做爱 |