Flowmatic:Go語言中結構化併發庫

banq發表於2024-05-26


Flowmatic 是一個通用的 Go 庫,它提供了一種類似Nurseries的結構化併發程式設計方法。它讓你能夠以簡單、有效且靈活的方式輕鬆管理併發任務。

Flowmatic 擁有易於使用的 API,其中包含處理常見併發模式的函式。它會自動處理生成工作程式、收集錯誤和傳播恐慌。

Flowmatic 需要 Go 1.20+。

特徵

  • 具有簡單的 API,可提高通道/等待組/互斥的可讀性
  • 處理各種併發問題,如異構任務組、切片上任務的同質執行以及動態工作生成
  • 彙總錯誤
  • 正確地跨 goroutine 邊界傳播恐慌
  • 有上下文取消助手
  • 依賴性低
  • 良好的測試覆蓋率

Flowmatic 能解決的一個問題是管理多個相互獨立的任務的並行執行。例如,假設您要向三個不同的下游應用程式介面傳送資料。如果任何一個傳送失敗,您都希望返回錯誤資訊。如果使用傳統的 Go 併發功能,這很快就會變得複雜而難以管理,因為需要跟蹤 Goroutines、channel 和 sync.WaitGroups。

Flowmatic 讓這一切變得簡單。

err := flowmatic.Do(
    func() error {
        return doThingA(),
    },
    func() error {
        return doThingB(),
    },
    func() error {
        return doThingC(),
    })

使用stdlib傳統:

var wg sync.WaitGroup
var errs []error
errChan := make(chan error)

wg.Add(3)
go func() {
    defer wg.Done()
    if err := doThingA(); err != nil {
        errChan <- err
    }
}()
go func() {
    defer wg.Done()
    if err := doThingB(); err != nil {
        errChan <- err
    }
}()
go func() {
    defer wg.Done()
    if err := doThingC(); err != nil {
        errChan <- err
    }
}()

go func() {
    wg.Wait()
    close(errChan)
}()

for err := range errChan {
    errs = append(errs, err)
}

err := errors.Join(errs...)


panic
在 Go 中,如果 goroutine 中出現 panic,並且該 panic 未被恢復,則整個程序將關閉。這種方法有利有弊。優點是,如果 panic 是應用程式中程式設計錯誤的症狀,則應用程式不會造成進一步的損害。缺點是,在許多情況下,這會導致在可能可恢復的情況下關閉。

因此,雖然 Go 標準 HTTP 伺服器會捕獲在其某個 HTTP 處理程式中發生的錯誤並繼續提供請求,但是標準 Go HTTP 伺服器無法捕獲在單獨的 goroutine 中發生的錯誤,而這些錯誤將導致整個伺服器離線。

Flowmatic 透過捕獲在其一個工作 goroutine 中發生的panic並在父 goroutine 中重新傳播它來解決此問題,以便可以在適當的級別捕獲和記錄panic。

背景:
這篇文章認為:
Go在沒有結構化控制流的情況下生成併發任務是有害的,會導致諸如資源清理不當、錯誤處理問題以及併發程式碼推理困難等問題。

  • Go 語句中斷自動資源清理:使用 go 語句生成任務時,沒有自動方法確保任務完成後正確清理這些任務使用的資源。這需要手動實現資源清理,這很容易出錯。
  • Go 語句中斷錯誤處理:現代語言提供了強大的錯誤處理機制,例如異常。然而,這些機制依賴於“當前呼叫者”這一可靠的概念,而當使用 go 語句生成任務時,這一概念就會失效。這會導致未處理的錯誤被默默丟棄。

Nurseries 結構化併發:

  • Nurseries 允許生成多個併發任務,但確保在nurseries 退出之前將它們重新連線在一起。這為併發操作提供了明確的控制流結構。
  • Nurseries 允許在退出時自動清理資源,並傳播錯誤以在 Nurseries 建立時進行處理。這可恢復錯誤處理並使併發程式碼推理變得更容易。

併發框架不應該採用可以在任何地方生成任務的原始“go”操作,而應該從頭開始構建像Nurseries 託兒所這樣的結構化構造。

 

相關文章