sgo: Structured Concurrency Infrastructure for Go.
sgo provides higher-level abstractions by encapsulating Go's concurrency primitives for common concurrent operations. Using sgo can significantly reduce the complexity of concurrent programming, thereby improving its efficiency and robustness.
The design philosophy of sgo originates from Notes on structured concurrency, or: Go statement considered harmful.
The article points out that using the go
keyword directly for concurrent programming is as primitive and harmful as using goto
statements for flow control in early FLOW-MATIC languages. This low-level concurrency control breaks code abstraction and hinders modular development and local reasoning. Just as structured programming replaced goto
with sequence, selection, and loop constructs, structured concurrency requires high-level abstractions to replace direct use of go
.
Based on practical experience, Go's concurrency requirements can be categorized into three typical scenarios:
-
Parallel execution and waiting
For example, executing multiple initialization tasks concurrently during program startup, then proceeding after all tasks complete.
-
Parallel computation and result collection
For example, splitting a large array into slices for concurrent summation, then aggregating the results.
-
Race execution and optimal selection
For example, querying multiple search engines concurrently and returning the first valid result.
These three requirements each have standard implementation patterns, but correctly and completely implementing them often requires:
- Rich experience
- Meticulous attention
- Comprehensive testing
Reinventing these patterns each time places significant cognitive burden on developers. Following the "structured concurrency" philosophy, encapsulating these patterns into unified abstract structures can systematically solve these problems. sgo encapsulates the above three patterns as:
-
sgo.Group
sgo.NewGroup(). Go(setupStep1). Go(setupStep2). Wait()
-
sgo.Collector
result := sgo.NewCollector[int](). Go(func() int { return sumSlice(s[:99]) }). Go(func() int { return sumSlice(s[99:]) }). Collect() sum := result[0] + result[1]
-
sgo.Racer
result := sgo.NewRacer[string](context.Background()). Go(func(ctx context.Context) string { return searchGoogle(ctx, keyword); }). Go(func(ctx context.Context) string { return searchBing(ctx, keyword); }). Collect()
The essence of structured concurrency lies in abstracting proven concurrent patterns into reusable programming components. This philosophy shares the same lineage as structured programming's transformation of goto
statements into standard control structures. Its core value lies not in the implementation technology itself, but in the programming paradigm innovation it advocates.