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

          用 10 分鐘了解 Go 語言 context 包使用場景

          共 1190字,需瀏覽 3分鐘

           ·

          2020-07-04 23:22

          點擊上方藍色“Go語言中文網”關注我們,領全套Go資料,每天學習?Go?語言

          context 是在 Go 語言 1.7 版才正式被公認的官方標準庫內,為什么今天要介紹 context 使用方式呢?原因很簡單,在初學 Go 時,寫 API 時,常常不時就會看到在 http handler 的第一個參數就會是ctx context.Context,而這個 context 在這邊使用的目的和含義到底是什么呢,本篇就是帶大家了解什么是 context,以及使用的場景及方式,內容不會提及 context 的原碼,而是用幾個實際例子來了解。

          使用 WaitGroup

          學 Go 時肯定要學習如何使用并發(fā)(goroutine),而開發(fā)者該如何控制并發(fā)呢?其實有兩種方式,一種是WaitGroup,另一種就是 context,而什么時候需要用到 WaitGroup 呢?很簡單,就是當您需要將同一件事情拆成不同的 Job 進行執(zhí)行,最后需要等到全部的 Job 都執(zhí)行完成才繼續(xù)執(zhí)行主程序,這時候就需要用到 WaitGroup,看個實際例子

          package?main

          import?(
          ????"fmt"
          ????"sync"
          ????"time"
          )

          func?main()?{
          ????var?wg?sync.WaitGroup

          ????wg.Add(2)
          ????go?func()?{
          ????????time.Sleep(2?*?time.Second)
          ????????fmt.Println("job?1?done.")
          ????????wg.Done()
          ????}()
          ????go?func()?{
          ????????time.Sleep(1?*?time.Second)
          ????????fmt.Println("job?2?done.")
          ????????wg.Done()
          ????}()
          ????wg.Wait()
          ????fmt.Println("All?Done.")
          }

          上面范例可以看到主程序穿透wg.Wait()來等待全部工作都執(zhí)行完成,才印出最后的消息。這邊會遇到一個情境就是,雖然把工作拆成多個,并且丟到背景去跑,可是使用者該如何透過其他方式來終止相關的 goroutine 工作呢(像是開發(fā)者都會寫背景程式監(jiān)控,需要連續(xù)執(zhí)行)?例如 UI 上方有停止的按鈕,點繼續(xù)后,如何主動通知并且停止正在運行的 Job,這邊很簡單,可以使用 channel + select 方式。

          使用 channel + select

          package?main

          import?(
          ????"fmt"
          ????"time"
          )

          func?main()?{
          ????stop?:=?make(chan?bool)

          ????go?func()?{
          ????????for?{
          ????????????select?{
          ????????????case?<-stop:
          ????????????????fmt.Println("got?the?stop?channel")
          ????????????????return
          ????????????default:
          ????????????????fmt.Println("still?working")
          ????????????????time.Sleep(1?*?time.Second)
          ????????????}
          ????????}
          ????}()

          ????time.Sleep(5?*?time.Second)
          ????fmt.Println("stop?the?gorutine")
          ????stop?<-?true
          ????time.Sleep(5?*?time.Second)
          }

          上面可以看到,透過 select + channel 可以快速解決這個問題,只要在任何地方將 bool 值丟入 stop channel 就可以停止背景正在處理的 Job。上述用 channel 來解決此問題,但是現在有個問題,假設背景有跑了無數個 goroutine,或者是 goroutine 內又有跑 goroutine 呢,變得相當復雜,例如底下的狀況

          e25de01a8ae34477915d2e833a7eda1a.webp

          這邊就沒辦法用 channel 方式來進行處理了,而需要用到今天的重點 context。

          認識 context

          從上圖可以看到我們建立了三個 worker 節(jié)點來處理不同的 Job,所以會在主程序最上面的宣告一個主context.Background(),然后在每個 worker 節(jié)點分別在單獨建立子上下文中,其最主要目的就是當關閉其中一個上下文就可以直接取消該 worker 內在跑的 Job。拿上面的例子進行改寫

          package?main

          import?(
          ????"context"
          ????"fmt"
          ????"time"
          )

          func?main()?{
          ????ctx,?cancel?:=?context.WithCancel(context.Background())

          ????go?func()?{
          ????????for?{
          ????????????select?{
          ????????????case?<-ctx.Done():
          ????????????????fmt.Println("got?the?stop?channel")
          ????????????????return
          ????????????default:
          ????????????????fmt.Println("still?working")
          ????????????????time.Sleep(1?*?time.Second)
          ????????????}
          ????????}
          ????}()

          ????time.Sleep(5?*?time.Second)
          ????fmt.Println("stop?the?gorutine")
          ????cancel()
          ????time.Sleep(5?*?time.Second)
          }

          其實可以看到只是把原本的頻道換成使用 context 來處理,其他完全不變,這邊提到使用了context.WithCancel,使用底下方式可以擴展 context

          ctx,?cancel?:=?context.WithCancel(context.Background())

          這用意在于每個 worknode 都有獨立的cancel func開發(fā)者可以透過其他地方呼叫 cancel()來決定哪一個工人需要被停止,這時候可以做到使用 context 來停止多個 goroutine 的效果,底下看看實際例子

          package?main

          import?(
          ????"context"
          ????"fmt"
          ????"time"
          )

          func?main()?{
          ????ctx,?cancel?:=?context.WithCancel(context.Background())

          ????go?worker(ctx,?"node01")
          ????go?worker(ctx,?"node02")
          ????go?worker(ctx,?"node03")

          ????time.Sleep(5?*?time.Second)
          ????fmt.Println("stop?the?gorutine")
          ????cancel()
          ????time.Sleep(5?*?time.Second)
          }

          func?worker(ctx?context.Context,?name?string)?{
          ????for?{
          ????????select?{
          ????????case?<-ctx.Done():
          ????????????fmt.Println(name,?"got?the?stop?channel")
          ????????????return
          ????????default:
          ????????????fmt.Println(name,?"still?working")
          ????????????time.Sleep(1?*?time.Second)
          ????????}
          ????}
          }

          上面透過一個 context 可以一次停止多個 worker,看邏輯如何宣告 context 以及什么時機去執(zhí)行 cancel(),通常我個人都是搭配 graceful shutdown 進行取消運轉的工作,或者是停止資料庫連線等等。。

          心得

          初學 Go 時,如果還不常使用 goroutine,其實也不會理解到 context 的使用方式及時機,等到需要有背景處理,以及該如何停止 Job,這時候才漸進了解到使用方式,當然 context 不只有這個使用方式,未來將會介紹其他使用方式。

          作者:ApplyBoy

          原文鏈接:https://blog.wu-boy.com/2020/05/understant-golang-context-in-10-minutes/


          推薦閱讀



          喜歡本文的朋友,歡迎關注“Go語言中文網

          Go語言中文網啟用微信學習交流群,歡迎加微信:274768166,投稿亦歡迎


          瀏覽 93
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  国内视频一区 | 日本色情在线视频 | 青青在线| 红桃视频国产在线 | 日韩成人AV电影 |