Go Context 到底放第一個參數傳,還是放結構體裏?
大家好,我是煎魚。
前段時間我們在聊手動管理內存 arena 的後續時。我們有提到 context 的函數傳參等問題。
當時在評論區有許多的小夥伴交流了起來,大家對此還是非常關注的:
今天我們就來聊一聊 conetxt 傳參的這個事。到底擺哪?Go 官方推薦是什麼?
快速介紹
上下文(Context)是 Go 語言中非常有特色的一個特性,其主要的作用是在 goroutine 中進行上下文的傳遞,而在傳遞信息中又包含了 goroutine 的運行控制、上下文信息傳遞等功能。
以下是一個簡單 Demo:
func main() {
parentCtx := context.Background()
ctx, cancel := context.WithTimeout(parentCtx, 1*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
f1(ctx)
case <-ctx.Done():
f2(ctx)
}
}
func f1(ctx context.Context) {
fmt.Println("腦子進煎魚了", ctx.Err())
}
func f2(ctx context.Context) {
fmt.Println("煎魚進腦子了", ctx.Err())
}
輸出結果:
煎魚進腦子了 context deadline exceeded
Go context 放哪好的爭議點
根據上面的 Demo,有爭議的點在哪?就是這個函數里 context,到底怎麼傳。放函數第一個參數,還是放結構體?
// 第一種方式
func f1(ctx context.Context) {...}
// 第二種方式
type T struct {
Ctx context
}
-
放在函數第一個參數 context 中的話,那每個方法都需要進行傳遞,用起來就像被 ” 污染 “ 了一樣。每個函數都要層層遞進的傳下去。非常的顯性。
-
放在結構體中的話,按理說 context 是基本對標請求的,每次請求都是不同的 context。這樣可能會導致每次都要
NewT()或是WithContext。一旦忘了,就很容易埋下隱患。
Go 官方建議
Go 官方建議中是怎麼說的?在 context 標準庫的文檔中有提到。使用上下文的程序應遵循以下規則,以保持跨包的接口一致:
-
不要將 context 存儲在結構體中。
-
應該將 context 顯式傳遞給每個需要它的函數,其應該是第一個參數,通常將參數命名爲 ctx。
-
不要傳遞 nil context。如果您不確定要使用哪個 context,請傳遞
context.TODO。
對應的示例代碼:
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
劃重點:官方建議在函數首位參數傳遞 context,不建議使用結構體傳遞。
一些討論
雖然官方是這麼建議了。但看起來沒頭沒尾,不認可的人也有不少。
包括前 Go 核心團隊的 @Brad Fitzpatrick 也表達了反對的意見,並給出進一步的詮釋:
While we've told people not to add contexts to structs, I think that guidance is over-aggressive. The real advice is not to store contexts. They should be passed along like parameters. But if the struct is essentially just a parameter, it's okay. I think this concern can be addressed with package-level documentation and examples.
大概意思:我們曾告訴人們不要在結構體中添加上下文,但我認爲這種指導過於激進。真正的建議是不要存儲上下文。它們應該像參數一樣傳遞。但如果結構體本質上只是一個參數,那就沒有問題。我認爲可以通過包級文檔和示例來解決這個問題。
可能有同學想說,那是不是 Go 官方就可以放寬 context 的使用建議了?
想太多了... 早在 2017 年就有 @Caleb Spare 提出《context: relax recommendation against putting Contexts in structs[1]》,希望 Go 官方放寬對於 context 放入結構體中的建議。
但還是處於長期的阻塞狀態,沒有進一步的相關信息。@Russ Cox 被 @ 了也並沒有出來說任何的建議。
可以認爲官方文檔上的態度沒有什麼變化。
總結
Context 在 Go 中是一個非常重要的特性,基本覆蓋編程的方方面面,只要和 “控制” 相關的,都有他的存在。
在官方的建議中,是不推薦使用結構體存儲 context 的,更推薦使用函數第一個參數傳遞 context 並命名爲 ctx。進一步詮釋的話,指的是不希望存儲 context。
大家在 context 實踐中又是使用哪種方式呢?歡迎分享。
參考資料
[1]
context: relax recommendation against putting Contexts in structs: https://github.com/golang/go/issues/22602
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/DZl_lIueSHpdjfWJU9RrHg