<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 421字,需瀏覽 1分鐘

           ·

          2021-10-28 22:53

          大家好,我是煎魚。

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

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

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

          goroutine 案例

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

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

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

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

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

          關閉 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

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

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

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

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

          定期輪詢 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 的關閉。

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

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

          使用 context

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

          代碼如下:

          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)體。可用于識別當前 channel 是否已經(jīng)被關閉,其原因可能是到期,也可能是被取消了。

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

          干掉另外一個 goroutine

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

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

          go/issues/32610

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

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

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

          總結(jié)

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

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

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

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

          關注煎魚,吸取他的知識???



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

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

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

          瀏覽 103
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩高清无码 | 精品亲子乱一区二区三区 | 日韩小电影在线 | 亚洲国产视频在线观看 | 国产精品盗摄!偷窥盗摄 |