1 回答

TA貢獻1840條經驗 獲得超5個贊
在您的具體示例中,代碼量足夠小,理解它是如何工作的可能沒有問題。當您更換function1并function2使用更復雜的東西時,問題就開始了。您鏈接到的文章給出了為什么傳遞取消上下文可以做一些難以推理的事情的具體原因,但更一般的原則是您應該嘗試將協調工作(取消,旋轉 goroutines)與底層工作分開以盡可能地完成(無論做什么function1,function2正在做什么)。這只是有助于更容易獨立地推理代碼的子部分,并有助于使測試更容易?!?do <something>”比“ do <something> function2”更容易理解function2function1”。
無需將取消函數傳遞給function2,您只需在生成的 gooutune 中調用它即可運行function2:
func main() {
//...
go func() {
function2(ctxWithCancel)
cancelFunction()
}()
//...
}
這是侄女,因為確定何時取消的協調工作全部包含在調用函數中,而不是分散在多個函數中。
如果你想function2有條件地取消上下文,讓它顯式返回某種值,指示是否發生了一些可取消的條件:
func function2(ctx context.Context) bool {
//...
if workShouldBecanceled() {
return true
}
//...
return false
}
func main() {
//...
go func() {
if function2(ctxWithCancel) {
cancelFunction()
}
}()
//...
}
這里我使用了一個布爾值,但通常這種模式與errors 一起使用 - 如果function2返回一個非 nil error,則取消其余的工作。
根據你在做什么,類似的東西errgroup.WithContext可能對你有用。這可以協調多個并發操作,所有這些操作都可能失敗,并在第一個操作失敗后立即取消其他操作。
我嘗試在上下文取消中遵循的另一種最佳實踐:始終確保取消函數在某個時候被調用。在docs中,兩次調用取消函數是安全的,所以我經常會做這樣的事情:
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
//...
if shouldCancel() {
cancel()
}
//...
}
編輯以回應評論:
如果您有多個長時間運行的操作(例如,服務器、連接等),并且您希望在第一個操作停止后立即關閉所有操作,那么上下文取消是一種合理的方法。但是,我仍然建議您在單個函數中處理所有上下文交互。像這樣的東西會起作用:
func operation1(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
}
//...
}
}
func operation2(ctx context.Context) {
// Similar code to operatoin1()
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
defer cancel()
operation1(ctx)
}()
go func() {
defer wg.Done()
defer cancel()
operation2(ctx)
}()
wg.Wait()
}
一旦其中一個操作終止,另一個操作將被取消,但main仍等待兩者完成。這兩個操作都不需要擔心管理這個問題。
- 1 回答
- 0 關注
- 139 瀏覽
添加回答
舉報