最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一篇文章搞懂Go語言中的Context

 更新時間:2022年07月04日 09:36:13   作者:??BarryYan????  
這篇文章主要介紹了一篇文章搞懂Go語言中的Context,Context攜帶一個截止日期、一個取消信號和其他跨越API邊界的值。上下文的方法可以被多個gor例程同時調用

0 前置知識sync.WaitGroup

sync.WaitGroup是等待一組協(xié)程結束。它實現(xiàn)了一個類似任務隊列的結構,可以向隊列中加入任務,任務完成后就把任務從隊列中移除,如果隊列中的任務沒有全部完成,隊列就會觸發(fā)阻塞以阻止程序繼續(xù)運行。 sync.WaitGroup只有3個方法,Add(),Done(),Wait() 。

其中Done()是Add(-1)的別名,使用Add()添加計數(shù),Done()減掉一個計數(shù),計數(shù)不為0, 阻塞Wait()的運行。

示例:

package main
import (
 ? "fmt"
 ? "sync"
 ? "time"
)
var group sync.WaitGroup
func sayHello() {
 ? for i := 0; i < 5; i++ {
 ? ? ?fmt.Println("hello......")
 ? ? ?time.Sleep(time.Second)
 ? }
 ? //線程結束 -1
 ? group.Done()
}
func sayHi() {
 ? //線程結束 -1
 ? defer group.Done()
 ? for i := 0; i < 5; i++ {
 ? ? ?fmt.Println("hi......")
 ? ? ?time.Sleep(time.Second)
 ? }
}
func main() {
 ? //+2
 ? group.Add(2)
 ? fmt.Println("main正在阻塞...")
 ? go sayHello()
 ? fmt.Println("main持續(xù)阻塞...")
 ? go sayHi()
 ? //線程等待
 ? group.Wait()
 ? fmt.Println("main貌似結束了阻塞...")
}

效果:

1 簡介

在 Go 服務器中,每個傳入請求都在其自己的 goroutine 中處理。請求處理程序通常會啟動額外的 goroutine 來訪問后端,例如數(shù)據(jù)庫和 RPC 服務。處理請求的一組 goroutine 通常需要訪問特定于請求的值,例如最終用戶的身份、授權令牌和請求的截止日期。當請求被取消或超時時,處理該請求的所有 goroutine 都應該快速退出,以便系統(tǒng)可以回收它們正在使用的任何資源。

為此,開發(fā)了一個context包,可以輕松地將請求范圍的值、取消信號和截止日期跨 API 邊界傳遞給處理請求所涉及的所有 goroutine。

Context攜帶一個截止日期、一個取消信號和其他跨越API邊界的值。上下文的方法可以被多個gor例程同時調用。

對服務器的傳入請求應該創(chuàng)建一個上下文,對服務器的傳出調用應該接受一個上下文。它們之間的函數(shù)調用鏈必須傳播 Context,可選擇將其替換為使用 WithCancel、WithDeadline、WithTimeout 或 WithValue 創(chuàng)建的派生 Context。當一個上下文被取消時,所有從它派生的上下文也被取消。

WithCancel、WithDeadline 和 WithTimeout 函數(shù)采用 Context(父)并返回派生的 Context(子)和 CancelFunc。調用 CancelFunc 會取消子項及其子項,刪除父項對子項的引用,并停止任何關聯(lián)的計時器。調用 CancelFunc 失敗會泄漏子項及其子項,直到父項被取消或計時器觸發(fā)。go vet 工具檢查是否在所有控制流路徑上使用了 CancelFuncs。

使用上下文的程序應遵循以下規(guī)則,以保持跨包的接口一致,并啟用靜態(tài)分析工具來檢查上下文傳播:

不要將上下文存儲在結構類型中;相反,將 Context 顯式傳遞給需要它的每個函數(shù)。

Context 應該是第一個參數(shù),通常命名為 ctx:

func DoSomething(ctx context.Context, arg Arg) error { 
    // ... 使用 ctx ... 
}

即使函數(shù)允許,也不要傳遞 nil 上下文。如果不確定要使用哪個 Context,請傳遞 context.TODO。

僅將上下文值用于傳輸流程和 API 的請求范圍數(shù)據(jù),而不用于將可選參數(shù)傳遞給函數(shù)。

相同的 Context 可以傳遞給在不同的 goroutine 中運行的函數(shù);上下文對于多個 goroutine 同時使用是安全的。

2 context.Context引入

//上下文攜帶截止日期、取消信號和請求范圍的值在API的界限。它的方法是安全的同時使用多個了goroutine。
type Context interface {
 ? ?// Done返回一個在上下文被取消或超時時關閉的通道。
 ? ?Done() <-chan struct{}
?
 ? ?// Err表示在Done通道關閉后為何取消此上下文。
 ? ?Err() error
?
 ? ?// Deadline返回上下文將被取消的時間(如果有的話)。
 ? ?Deadline() (deadline time.Time, ok bool)
?
 ? ?// Value返回與key相關的值,如果沒有則返回nil。
 ? ?Value(key interface{}) interface{}
}
  • Done方法返回一個通道,該通道作為代表運行的函數(shù)的取消信號Context:當通道關閉時,函數(shù)應該放棄它們的工作并返回。
  • Err方法返回一個錯誤,指示Context取消的原因。
  • 一個Context對于多個 goroutine 同時使用是安全的。代碼可以將單個傳遞Context給任意數(shù)量的 goroutines 并取消它Context以向所有goroutine 發(fā)出信號。
  • Deadline方法允許函數(shù)確定它們是否應該開始工作,還可以使用截止日期來設置 I/O 操作的超時時間。
  • Value允許一個Context攜帶請求范圍的數(shù)據(jù)。該數(shù)據(jù)必須是安全的,以便多個 goroutine 同時使用。

3 context包的其他常用函數(shù)

3.1 context.Background和context.TODO

Background是任何Context樹的根,它永遠不會被取消:

//Background返回一個空的Context。 它永遠不會取消,沒有截止日期,沒有價值。 Background通常用于main、init和tests,并作為傳入請求的頂級上下文。 ?
func Background() Context

給一個函數(shù)方法傳遞Context的時候,不要傳遞nil,如果不知道傳遞什么,就使用context.TODO()

3.2 context.WithCancel和

WithCancelt返回派生的Context值,可以比父Context更快地取消。當請求處理程序返回時,通常會取消與傳入請求關聯(lián)的content。當使用多個副本時,WithCancel對于取消冗余請求也很有用。

// WithCancel返回一個父進程的副本,該父進程的Done通道被盡快關閉。?關閉Done或調用cancel。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// CancelFunc取消一個上下文。
type CancelFunc func()

示例:

package main
import (
 ? "context"
 ? "fmt"
)
func play(ctx context.Context) <-chan int {
 ? dist := make(chan int)
 ? n := 1
 ? //匿名函數(shù) 向dist中加入元素
 ? go func() {
 ? ? ?for {
 ? ? ? ? select {
 ? ? ? ? //ctx為空時將不會執(zhí)行這個
 ? ? ? ? case <-ctx.Done():
 ? ? ? ? ? ?return // return結束該goroutine,防止泄露
 ? ? ? ? ? ?//向dist中加入元素
 ? ? ? ? case dist <- n:
 ? ? ? ? ? ?n++
 ? ? ? ? }
 ? ?  }
 ? }()
 ? return dist
}
func main() {
 ? //返回空的context
 ? ctx, cancel := context.WithCancel(context.Background())
 ? defer cancel() // 調用cancel
 ? for n := range play(ctx) {
 ? ? ?fmt.Println(n)
 ? ? ?if n == 5 {
 ? ? ? ? break
 ? ?  }
 ? }
}

擴展:go中select的用法

```
select的用法與switch語言非常類似,由select開始一個新的選擇塊,每個選擇條件由case語句來描述。
與switch語句相比, select有比較多的限制,其中最大的一條限制就是每個case語句里必須是一個IO操作,大致的結構如下:
``` go
select {
 ? case <-chan1:
 ? ? ?// 如果chan1成功讀到數(shù)據(jù),則進行該case處理語句
 ? case chan2 <- 1:
 ? ? ?// 如果成功向chan2寫入數(shù)據(jù),則進行該case處理語句
 ? default:
 ? ? ?// 如果上面都沒有成功,則進入default處理流程
}
```
在一個select語句中,Go語言會按順序從頭至尾評估每一個發(fā)送和接收的語句。
如果其中的任意一語句可以繼續(xù)執(zhí)行(即沒有被阻塞),那么就從那些可以執(zhí)行的語句中任意選擇一條來使用。
如果沒有任意一條語句可以執(zhí)行(即所有的通道都被阻塞),那么有兩種可能的情況:
- 如果給出了default語句,那么就會執(zhí)行default語句,同時程序的執(zhí)行會從select語句后的語句中恢復。
- 如果沒有default語句,那么select語句將被阻塞,直到至少有一個通信可以進行下去。
```

3.3 context.WithTimeout

WithTimeout返回派生的Context值,WithTimeout用于設置請求到后端服務器的截止日期:

//WithTimeout返回一個父進程的副本,該父進程的Done通道被立即關閉的父母。關閉“完成”、調用“取消”或超時結束。新
//Context的Deadline是現(xiàn)在的更快+timeout和父的Deadline,如果任何。?如果計時器仍然在運行,則cancel函數(shù)釋放它資源。
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
// CancelFunc取消一個上下文。
type CancelFunc func()

示例:

package main
import (
 ? "context"
 ? "fmt"
 ? "sync"
 ? "time"
)
var wg sync.WaitGroup
func worker(ctx context.Context) {
 ? ?LOOP:
 ? for {
 ? ? ?fmt.Println("db connecting ...")
 ? ? ?time.Sleep(time.Millisecond * 10) // 假設正常連接數(shù)據(jù)庫耗時10毫秒
 ? ? ?select {
 ? ? ?case <-ctx.Done(): // 50毫秒后自動調用
 ? ? ? ? break LOOP
 ? ? ?default:
 ? ?  }
 ? }
 ? fmt.Println("worker done!")
 ? wg.Done()
}
func main() {
 ? // 設置一個50毫秒的超時
 ? ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
 ? wg.Add(1)
 ? go worker(ctx)
 ? time.Sleep(time.Second * 5)
 ? cancel() // 通知子goroutine結束
 ? wg.Wait()
 ? fmt.Println("over")
}

執(zhí)行結果:

3.4 context.WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
 ? if parent == nil {
 ? ? ?panic("cannot create context from nil parent")
 ? }
 ? if cur, ok := parent.Deadline(); ok && cur.Before(d) {
 ? ? ?// 目前的期限已經(jīng)比新的期限提前
 ? ? ?return WithCancel(parent)
 ? }
 ? c := &timerCtx{
 ? ? ?cancelCtx: newCancelCtx(parent),
 ? ? ?deadline: ?d,
 ? }
 ? propagateCancel(parent, c)
 ? dur := time.Until(d)
 ? if dur <= 0 {
 ? ? ?c.cancel(true, DeadlineExceeded) // 截止日期已經(jīng)過去了
 ? ? ?return c, func() { c.cancel(false, Canceled) }
 ? }
 ? c.mu.Lock()
 ? defer c.mu.Unlock()
 ? if c.err == nil {
 ? ? ?c.timer = time.AfterFunc(dur, func() {
 ? ? ? ? c.cancel(true, DeadlineExceeded)
 ? ?  })
 ? }
 ? return c, func() { c.cancel(true, Canceled) }
}

示例:

package main
import (
 ? "context"
 ? "fmt"
 ? "time"
)
func main() {
 ? d := time.Now().Add(500 * time.Millisecond)
 ? ctx, cancel := context.WithDeadline(context.Background(), d)
 ? // 盡管ctx會過期,但在任何情況下調用它的cancel函數(shù)都是很好的實踐。
 ? // 如果不這樣做,可能會使上下文及其父類存活的時間超過必要的時間。
 ? defer cancel()
 ? select {
 ? case <-time.After(1 * time.Second):
 ? ? ?fmt.Println("over")
 ? case <-ctx.Done():
 ? ? ?fmt.Println(ctx.Err())
 ? }
}

執(zhí)行結果:

3.5 context.WithValue

WithValue提供了一種將請求范圍的值與Context關聯(lián)的方法 :

//WithValue返回父元素的副本,其Value方法返回val for key。
func WithValue(parent Context, key interface{}, val interface{}) Context

了解如何使用context包的最好方法是通過一個已工作的示例。

示例:

package main
import (
 ? "context"
 ? "fmt"
 ? "sync"
 ? "time"
)
type TraceCode string
var wg sync.WaitGroup
func worker(ctx context.Context) {
 ? key := TraceCode("KEY_CODE")
 ? traceCode, ok := ctx.Value(key).(string) // 在子goroutine中獲取trace code
 ? if !ok {
 ? ? ?fmt.Println("invalid trace code")
 ? }
 ? ?LOOP:
 ? for {
 ? ? ?fmt.Printf("worker,code:%s\n", traceCode)
 ? ? ?time.Sleep(time.Millisecond * 10) // 假設正常連接數(shù)據(jù)庫耗時10毫秒
 ? ? ?select {
 ? ? ?case <-ctx.Done(): // 50毫秒后自動調用
 ? ? ? ? break LOOP
 ? ? ?default:
 ? ?  }
 ? }
 ? fmt.Println("worker is over!")
 ? wg.Done()
}
?
func main() {
 ? // 設置一個50毫秒的超時
 ? ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
 ? // 在系統(tǒng)的入口中設置trace code傳遞給后續(xù)啟動的goroutine實現(xiàn)日志數(shù)據(jù)聚合
 ? ctx = context.WithValue(ctx, TraceCode("KEY_CODE"), "12512312234")
 ? wg.Add(1)
 ? go worker(ctx)
 ? time.Sleep(time.Second * 5)
 ? cancel() // 通知子goroutine結束
 ? wg.Wait()
 ? fmt.Println("over")
}

執(zhí)行結果:

4 實例:請求瀏覽器超時

server端:

package main
import (
 ? "fmt"
 ? "math/rand"
 ? "net/http"
 ? "time"
)
// server端,隨機出現(xiàn)慢響應
func indexHandler(w http.ResponseWriter, r *http.Request) {
 ? number := rand.Intn(2)
 ? if number == 0 {
 ? ? ?time.Sleep(time.Second * 10) // 耗時10秒的慢響應
 ? ? ?fmt.Fprintf(w, "slow response")
 ? ? ?return
 ? }
 ? fmt.Fprint(w, "quick response")
}
func main() {
 ? http.HandleFunc("/", indexHandler)
 ? err := http.ListenAndServe(":9999", nil)
 ? if err != nil {
 ? ? ?panic(err)
 ? }
}

client端:

package main
import (
 ? "context"
 ? "fmt"
 ? "io/ioutil"
 ? "net/http"
 ? "sync"
 ? "time"
)
// 客戶端
?
type respData struct {
 ? resp *http.Response
 ? err ?error
}
func doCall(ctx context.Context) {
 ? // http長連接
 ? transport := http.Transport{DisableKeepAlives: true}
 ? client := http.Client{Transport: &transport}
?
 ? respChan := make(chan *respData, 1)
 ? req, err := http.NewRequest("GET", "http://127.0.0.1:9999/", nil)
 ? if err != nil {
 ? ? ?fmt.Println(err)
 ? ? ?return
 ? }
 ? req = req.WithContext(ctx) // 使用帶超時的ctx創(chuàng)建一個新的client request
 ? var wg sync.WaitGroup
 ? wg.Add(1)
 ? defer wg.Wait()
 ? go func() {
 ? ? ?resp, err := client.Do(req)
 ? ? ?fmt.Printf("resp:%v, err:%v\n", resp, err)
 ? ? ?rd := &respData{
 ? ? ? ? resp: resp,
 ? ? ? ? err: ?err,
 ? ?  }
 ? ? ?respChan <- rd
 ? ? ?wg.Done()
 ? }()
 ? select {
 ? case <-ctx.Done():
 ? ? ?fmt.Println("timeout...")
 ? case result := <-respChan:
 ? ? ?fmt.Println("success....")
 ? ? ?if result.err != nil {
 ? ? ? ? fmt.Printf("err:%v\n", result.err)
 ? ? ? ? return
 ? ?  }
 ? ? ?defer result.resp.Body.Close()
 ? ? ?data, _ := ioutil.ReadAll(result.resp.Body)
 ? ? ?fmt.Printf("resp:%v\n", string(data))
 ? }
}
?
func main() {
 ? // 定義一個100毫秒的超時
 ? ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
 ? defer cancel() // 調用cancel釋放子goroutine資源
 ? doCall(ctx)
}

5 Context包都在哪些地方使用

許多服務器框架提供了用于承載請求作用域值的包和類型。我們可以定義“Context”接口的新實現(xiàn),在使用現(xiàn)有框架的代碼和需要“Context”參數(shù)的代碼之間架起橋梁。

6 小結

在谷歌中,要求Go程序員將“Context”參數(shù)作為傳入和傳出請求之間的調用路徑上的每個函數(shù)的第一個參數(shù)傳遞。這使得許多不同團隊開發(fā)的Go代碼能夠很好地互操作。它提供了對超時和取消的簡單控制,并確保像安全憑證這樣的關鍵值能夠正確地傳輸Go程序。

想要構建在“Context”上的服務器框架應該提供“Context”的實現(xiàn)來連接它們的包和那些需要“Context”參數(shù)的包。然后,它們的客戶端庫將接受來自調用代碼的“Context”。通過為請求范圍的數(shù)據(jù)和取消建立一個公共接口,“上下文”使包開發(fā)人員更容易共享創(chuàng)建可伸縮服務的代碼。

到此這篇關于一篇文章搞懂Go語言中的Context的文章就介紹到這了,更多相關 Go Context內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go anko實現(xiàn)支持腳本語言

    Go anko實現(xiàn)支持腳本語言

    anko是一個可以讓 Go 項目支持腳本語言的小工具,換句話說,就是可以給 Go 項目加點“腳本魔法”,下面就跟隨小編一起來學習一下他的具體使用吧
    2024-11-11
  • golang實現(xiàn)微信小程序商城后臺系統(tǒng)(moshopserver)

    golang實現(xiàn)微信小程序商城后臺系統(tǒng)(moshopserver)

    這篇文章主要介紹了golang實現(xiàn)微信小程序商城后臺系統(tǒng)(moshopserver),本文通過截圖實例代碼的形式給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • go語言實現(xiàn)二叉樹的序例化與反序列化

    go語言實現(xiàn)二叉樹的序例化與反序列化

    這篇文章主要介紹了go語言實現(xiàn)二叉樹的序例化與反序列化,文章圍繞主題展開詳細內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • 一文帶你了解Go語言中方法的調用

    一文帶你了解Go語言中方法的調用

    這篇文章主要和大家分享一下Go語言中的方法的調用,文中的示例代碼講解詳細,對我們學習Go語言有一定的幫助,需要的小伙伴可以參考一下
    2022-12-12
  • Go 高效截取字符串的一些思考

    Go 高效截取字符串的一些思考

    這篇文章主要介紹了Go 高效截取字符串的一些思考,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-10-10
  • Go中使用加密算法的方法

    Go中使用加密算法的方法

    本文通過實例代碼給大家介紹go中使用加密算法的方法,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-08-08
  • 詳解Golang如何比較兩個slice是否相等

    詳解Golang如何比較兩個slice是否相等

    開發(fā)中常會遇到需要比較兩個slice包含的元素是否完全相等的情況,我們通常會通過兩種方法去比較切片是否相等。這里通過幾個示例來看一下這兩種方法,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助
    2022-11-11
  • 深入了解Golang中的反射機制

    深入了解Golang中的反射機制

    反射是指在程序運行時動態(tài)地檢查和修改對象的能力,在Go語言中,通過反射可以在運行時檢查變量的類型、獲取結構體字段和方法的信息,以及動態(tài)調用方法等操作,本文將帶你深入了解Golang中的反射機制,感興趣的同學可以跟著小編一起來學習
    2023-05-05
  • Golang中的錯誤處理的示例詳解

    Golang中的錯誤處理的示例詳解

    這篇文章主要為大家詳細介紹了Golang中的錯誤處理的相關資料,文章中的示例代碼講解詳細,對我們學習Golang有一定幫助,需要的可以參考一下
    2022-11-11
  • Go語言命令行參數(shù)及cobra使用方法

    Go語言命令行參數(shù)及cobra使用方法

    Cobra是關于golang的一個命令行解析庫,用它能夠快速創(chuàng)建功能強大的 cli應用程序和命令行工具,本文主要介紹了Go語言命令行參數(shù)及cobra使用方法,感興趣的可以了解一下
    2024-01-01

最新評論

望江县| 通城县| 通州市| 南江县| 合山市| 阳朔县| 普陀区| 汽车| 安泽县| 马龙县| 巴南区| 溧阳市| 德庆县| 白河县| 安达市| 洞口县| 尼勒克县| 洛阳市| 尉氏县| 永安市| 尚志市| 亳州市| 墨竹工卡县| 原平市| 朔州市| 许昌县| 富阳市| 南江县| 卢湾区| 内丘县| 招远市| 渝北区| 思茅市| 阜新市| 建昌县| 威海市| 黔西| 郓城县| 兴宁市| 石首市| 通山县|