自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go 語言 context 優(yōu)秀實踐

開發(fā) 前端
本文我們介紹 context? 包的傳值、超時和取消的使用方式,context? 包的這三個功能,我們不僅可以用于跨 goroutine 的操作,而且還可以用于跨服務(wù)的操作。

?1.介紹

Go 語言在 v1.7 引入 context 包,關(guān)于它的使用方式,我們在之前的文章中已經(jīng)介紹過,感興趣的讀者朋友們可以翻閱。

本文我們介紹 context 包的最佳實踐,包括傳值、超時和取消。

2.傳值

我們可以使用 context? 包的 func WithValue() 函數(shù)傳遞數(shù)據(jù)。

func main() {
ctx := context.WithValue(context.Background(), "ctxKey1", "ctxVal")
go func(ctx context.Context) {
// 讀取 ctx 的 value
data, ok := ctx.Value("ctxKey1").(string)
if ok {
fmt.Printf("sub goroutine get value from parent goroutine, val=%s\n", data)
}
}(ctx)
time.Sleep(1 * time.Second)
}

輸出結(jié)果:

sub goroutine get value from parent goroutine, val=ctxVal

閱讀上面這段代碼,我們使用 func WithValue()? 函數(shù)創(chuàng)建一個 context?,并且傳遞 key 為 ctxKey1 的數(shù)據(jù)。

我們知道 context? 是并發(fā)安全的,所以我們每次使用 context? 傳遞一個新數(shù)據(jù),都需要使用 func WithValue()? 函數(shù)創(chuàng)建一個新的 context?,包裝一下 parent context。

傳遞多個數(shù)據(jù)

...
ctx := context.WithValue(context.Background(), "ctxKey1", "ctxVal")
ctx = context.WithValue(ctx, "ctxKey2", "ctxVal2")
ctx = context.WithValue(ctx, "ctxKey3", "ctxVal3")
...

閱讀上面這段代碼,我們可以發(fā)現(xiàn),如果使用 context? 傳遞多個數(shù)據(jù),就需要使用 func WithValue()? 創(chuàng)建多個 context。

雖然通過使用 func WithValue()? 創(chuàng)建多個 context 的方式,可以實現(xiàn)我們的需求,但是,它使代碼不再優(yōu)雅,并且性能也會降低。

怎么解決?

針對該場景,我們可以參考 gRPC? 框架的 metadata? 包的代碼。定義一個 map?,通過傳遞 map? 類型的值,實現(xiàn)需要使用 context 傳遞多個數(shù)據(jù)的需求。

func main() {
ctxVal := make(map[string]string)
ctxVal["k1"] = "v1"
ctxVal["k2"] = "v2"
ctx := context.WithValue(context.Background(), "ctxKey1", ctxVal)
go func(ctx context.Context) {
// 讀取 ctx 的 value
data, ok := ctx.Value("ctxKey1").(map[string]string)
if ok {
fmt.Printf("sub goroutine get value from parent goroutine, val=%+v\n", data)
}
}(ctx)
time.Sleep(1 * time.Second)
}

輸出結(jié)果:

sub goroutine get value from parent goroutine, val=map[k1:v1 k2:v2]

修改傳遞數(shù)據(jù)

使用 context? 包的 func WithValue()? 函數(shù)傳遞的數(shù)據(jù),不建議在傳輸過程中進(jìn)行修改,如果遇到在傳輸過程中需要修改數(shù)據(jù)的場景,我們可以使用 COW 的方式處理,從而避免 data race。

func main() {
ctxVal := make(map[string]string)
ctxVal["k1"] = "v1"
ctxVal["k2"] = "v2"
ctx := context.WithValue(context.Background(), "ctxKey1", ctxVal)
go func(ctx context.Context) {
// 讀取 ctx 的 value
data, ok := ctx.Value("ctxKey1").(map[string]string)
if ok {
ctxVal := make(map[string]string)
for k, v := range data {
ctxVal[k] = v
}
ctxVal["k3"] = "v3"
ctx = context.WithValue(ctx, "ctxKey1", ctxVal)
data, ok := ctx.Value("ctxKey1").(map[string]string)
if !ok {
fmt.Printf("sub goroutine get value from parent goroutine, val=%+v\n", nil)
}
fmt.Printf("sub goroutine get value from parent goroutine, val=%+v\n", data)
}
}(ctx)
time.Sleep(1 * time.Second)
}

輸出結(jié)果:

sub goroutine get value from parent goroutine, val=map[k1:v1 k2:v2 k3:v3]

閱讀上面這段代碼,我們通過 COW?(copy on write) 方式修改 context 傳遞的數(shù)據(jù)。

3.超時

我們可以使用 context? 包的 func WithTimeout() 函數(shù)設(shè)置超時時間,從而避免請求阻塞。

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}

輸出結(jié)果:

context deadline exceeded

閱讀上面這段代碼,我們使用 func WithTimeout()? 函數(shù)創(chuàng)建一個 1ms? 取消的 context?,使用 select ... case ...? 讀取 ctx.Done()?,從而取消監(jiān)聽該 context? 的 goroutine。

4.取消

我們可以使用 context? 包的 func WithCancel()? 函數(shù)取消操作,從而避免 goroutine 泄露。

func main() {
gen := func() <-chan int {
dst := make(chan int)
go func() {
var n int
for {
dst <- n
n++
}
}()
return dst
}
for n := range gen() {
fmt.Println(n)
if n == 5 {
break
}
}
time.Sleep(1 * time.Second)
}

輸出結(jié)果:

0
1
2
3
4
5

閱讀上面這段代碼,我們創(chuàng)建一個 gen()? 函數(shù),啟動一個 goroutine? 生成整數(shù),循環(huán)調(diào)用 gen()? 函數(shù)輸出生成的整數(shù),當(dāng)整數(shù)值為 5 時,停止循環(huán),從輸出結(jié)果看,沒有發(fā)現(xiàn)問題。

但是,實際上該段代碼會導(dǎo)致 goroutine? 泄露,因為 gen() 函數(shù)一直在無限循環(huán)。

怎么解決?

我們可以使用 func WithCancel()? 函數(shù)創(chuàng)建一個 context?,作為 gen()? 函數(shù)的第一個參數(shù),當(dāng)停止循環(huán)時,同時調(diào)用 context? 的 CancelFunc? 取消 gen()? 函數(shù)啟動的 goroutine。

func main() {
gen := func(ctx context.Context) <-chan int {
dst := make(chan int)
go func() {
var n int
for {
dst <- n
n++
}
}()
return dst
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
cancel()
break
}
}
time.Sleep(1 * time.Second)
}

輸出結(jié)果:

0
1
2
3
4
5

5.總結(jié)

本文我們介紹 context? 包的傳值、超時和取消的使用方式,context? 包的這三個功能,我們不僅可以用于跨 goroutine 的操作,而且還可以用于跨服務(wù)的操作。

責(zé)任編輯:武曉燕 來源: Golang語言開發(fā)棧
相關(guān)推薦

2023-11-01 08:08:50

Go語言傳遞請求

2022-04-18 09:41:14

Go架構(gòu)設(shè)計

2023-10-27 12:11:33

2023-11-06 08:14:51

Go語言Context

2023-09-21 22:02:22

Go語言高級特性

2024-06-05 14:35:26

2021-12-15 09:00:00

GraphQL安全漏洞

2021-01-26 05:19:56

語言Go Context

2014-09-01 09:57:11

Go產(chǎn)品環(huán)境最佳語言

2025-03-17 01:55:00

TCP服務(wù)迭代

2023-02-07 15:33:16

云遷移數(shù)據(jù)中心云計算

2020-05-25 11:14:59

代碼程序開發(fā)

2024-12-12 09:02:35

2024-07-19 08:36:39

2018-03-12 22:13:46

GO語言編程軟件

2019-11-22 15:27:07

技術(shù)漏洞管理網(wǎng)絡(luò)

2019-11-24 23:39:01

漏洞管理漏洞風(fēng)險

2024-04-11 10:20:57

JavaScript前端Web

2022-09-01 08:50:22

kubernetes容器

2019-12-16 12:11:53

Docker容器Kubernetes
點贊
收藏

51CTO技術(shù)棧公眾號