詳解Go語言如何利用上下文進行并發(fā)計算
在Go編程中,上下文(context
)是一個非常重要的概念,它包含了與請求相關(guān)的信息,如截止日期和取消信息,以及在請求處理管道中傳遞的其他數(shù)據(jù)。在并發(fā)編程中,特別是在處理請求時,正確處理上下文可以確保我們尊重和執(zhí)行請求中設(shè)定的限制,如截止時間。
讓我們通過一些代碼示例來探討如何在并發(fā)計算中使用上下文,以及如何在處理請求時尊重上下文所設(shè)定的截止日期和取消要求。
// download 函數(shù)用于下載給定 URL 的內(nèi)容。 func download(ctx context.Context, url string) (string, error) {...}
download
函數(shù)嘗試獲取給定 URL 的內(nèi)容。然而,需要注意的是,每個 URL 的下載內(nèi)容可能不同,因此下載所需的時間也可能不同。如果在截止日期之前未能完成 URL 的下載,該函數(shù)將返回一個錯誤(截止日期錯誤)。
現(xiàn)在,假設(shè)我們需要下載許多 URL,并且我們只有有限的時間來完成這些下載。我們可以使用 errgroup
來并發(fā)地進行下載,如果超過截止時間,我們將取消所有并發(fā)操作。
// downloadAll 函數(shù)并發(fā)地下載給定 URL 的內(nèi)容。 func downloadAll(ctx context.Context, urls []string) ([]string, error) { results := make([]string, len(urls)) g, ctx := errgroup.WithContext(ctx) for i := range len(urls) { g.Go(func() error { content, err := download(ctx, urls[i]) if err != nil { return err } results[i] = content return nil }) } if err := g.Wait(); err != nil { return nil, err } return results, nil }
在這個示例中,downloadAll
函數(shù)同時下載每個給定的 URL,并將相同的上下文傳遞給 download
函數(shù)。如果下載任何一個 URL 所需的時間超過了設(shè)定的截止時間,download
函數(shù)將失敗,從而導(dǎo)致整個并發(fā)流程也失敗,downloadAll
將返回一個截止日期錯誤。
除了下載這些 URL,我們還需要處理下載的內(nèi)容。例如,我們可能要對每個 URL 的內(nèi)容應(yīng)用某個過濾器(謂詞)。
// filter 函數(shù)檢查給定內(nèi)容是否符合給定的謂詞。 func filter(content string, pred func(string) bool) bool { return pred(content) }
請注意,過濾器既不需要上下文,也不進行任何跨邊界調(diào)用。過濾器函數(shù)不關(guān)心上游處理的截止日期。
使用 filter
函數(shù),我們可以定義一個過濾所有內(nèi)容的函數(shù)。
// filterAll 函數(shù)同時過濾所有給定的內(nèi)容。 func filterAll(contents []string, pred func(string) bool) []string { type Result struct { content string ok bool } results := make([]Result, len(contents)) g := errgroup.Group{} for i, content := range contents { g.Go(func() error { ok := filter(contents[i], pred) results[i] = Result{content: content, ok: ok} return nil }) } g.Wait() var filtered []string for _, r := range results { if r.ok { filtered = append(filtered, r.content) } } return filtered }
filterAll
函數(shù)調(diào)用 filter
函數(shù)來應(yīng)用謂詞到每個內(nèi)容上,但謂詞的應(yīng)用可能會花費一些時間,可能超過上下文設(shè)置的截止時間。由于 filter
函數(shù)不使用上下文,因此它不會因為截止日期錯誤而失敗。
我們需要重新定義 filterAll
,使其使用上下文并檢查其中的錯誤,而不管 filter
函數(shù)是否使用了上下文。
// filterAll 函數(shù)同時過濾所有內(nèi)容,并檢查上下文中的錯誤。 func filterAll(ctx context.Context, contents []string, pred func(string) bool) ([]string, error) { type Result struct { content string ok bool } results := make([]Result, len(contents)) g, ctx := errgroup.WithContext(ctx) for i, content := range contents { g.Go(func() error { if err := ctx.Err(); err != nil { return err } ok := filter(contents[i], pred) results[i] = Result{content: content, ok: ok} return nil }) } if err := g.Wait(); err != nil { return nil, err } var filtered []string for _, r := range results { if r.ok { filtered = append(filtered, r.content) } } return filtered, nil }
我們的新實現(xiàn) filterAll
函數(shù)會檢查上下文中的任何錯誤,即使上下文并未直接傳遞給下游函數(shù)(在本例中為 filter
)。如果發(fā)生了與上下文相關(guān)的截止日期(或任何其他錯誤),整個過濾過程就會失敗。
現(xiàn)在,讓我們完成對所有內(nèi)容的處理。
// processURLs 函數(shù)下載每個 URL 的內(nèi)容并對其進行過濾。 // // 處理必須在上下文截止日期內(nèi)完成。 func processURLs(ctx context.Context, urls []string) ([]string, error) { contents, err := downloadAll(ctx, urls) if err != nil { return nil, err } filtered, err := filterAll(ctx, contents, somePredicate) return filtered, err }
如果任何一個下載操作花費的時間過長,那么在嘗試獲取內(nèi)容時就會發(fā)生截止日期錯誤,因為上下文被直接用于 API 調(diào)用。因此,downloadAll
函數(shù)也會失敗,進而導(dǎo)致 processURLs
失敗。
如果所有的 URL 在截止日期內(nèi)都被正確下載,我們將繼
續(xù)對它們進行過濾。在對每個下載內(nèi)容進行過濾時,不使用上下文,但 filterAll
函數(shù)明確地檢查上下文中的錯誤,如果發(fā)生了與上下文相關(guān)的截止日期(或任何其他錯誤),整個過濾過程就會失敗。
有時候,僅僅使用 errgroup.WithContext
是不足以檢測到上下文中的截止日期或其他問題的,特別是當上下文未直接使用時。因此,我們應(yīng)該定期檢查是否仍在時間限制內(nèi),否則就會失敗。
最后,我們可以通過編寫 filterAll
的測試來確保我們正確地處理了類似的情況,以確保我們尊重與上下文相關(guān)的任何錯誤。
func TestContextError(t *testing.T) { ctx, done := context.WithTimeout(context.Background(), time.Nanosecond) defer done() // 生成我們想要應(yīng)用過濾器的一些數(shù)據(jù)。 var contents []string = testingContent() _, err := filterAll(ctx, contents, thePredicate) if err == nil { t.Errorf("filterAll() = %v, want error", err) } }
請注意,在測試中,我們期望 filterAll
會失敗,因為我們設(shè)置的超時時間只有一納秒。因此,上下文應(yīng)該因為超過截止時間而發(fā)生錯誤。如果在啟動 Goroutine 進行下載內(nèi)容過濾時不檢查 context.Err()
,我們將永遠不會處理此類錯誤。
到此這篇關(guān)于詳解Go語言如何利用上下文進行并發(fā)計算的文章就介紹到這了,更多相關(guān)Go并發(fā)計算內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言常見數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)詳解
這篇文章主要為大家學(xué)習(xí)介紹了Go語言中的常見數(shù)據(jù)結(jié)構(gòu)(channal、slice和map)的實現(xiàn),文中的示例代碼簡潔易懂,需要的可以參考一下2023-07-07golang中拿slice當queue和拿list當queue使用分析
這篇文章主要為大家介紹了golang?中拿slice當queue和拿list當queue使用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08MacOS中 VSCode 安裝 GO 插件失敗問題的快速解決方法
這篇文章主要介紹了MacOS中 VSCode 安裝 GO 插件失敗問題的快速解決方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05Go基礎(chǔ)教程系列之import導(dǎo)入包(遠程包)和變量初始化詳解
這篇文章主要介紹了Go基礎(chǔ)教程系列之import導(dǎo)包和初始化詳解,需要的朋友可以參考下2022-04-04Go語言中GORM存取數(shù)組/自定義類型數(shù)據(jù)
在使用gorm時往往默認的數(shù)據(jù)類型不滿足我們的要求,需要使用一些自定義數(shù)據(jù)類型作為字段類型,下面這篇文章主要給大家介紹了關(guān)于Go語言中GORM存取數(shù)組/自定義類型數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2023-01-01