https://github.com/asmsh/sema
A feature-rich concurrency manager for Go, providing an alternative to sync.WaitGroup, channel-based limiter and semaphore.Weighted, with support for select-based waiting and internal counter inspection.
https://github.com/asmsh/sema
concurrency go goroutine limiter
Last synced: 11 months ago
JSON representation
A feature-rich concurrency manager for Go, providing an alternative to sync.WaitGroup, channel-based limiter and semaphore.Weighted, with support for select-based waiting and internal counter inspection.
- Host: GitHub
- URL: https://github.com/asmsh/sema
- Owner: asmsh
- License: apache-2.0
- Created: 2025-06-29T20:58:07.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2025-06-29T22:28:38.000Z (12 months ago)
- Last Synced: 2025-06-29T22:32:28.006Z (12 months ago)
- Topics: concurrency, go, goroutine, limiter
- Language: Go
- Homepage:
- Size: 24.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Sema: A feature-rich concurrency manager for Go.
[](https://pkg.go.dev/github.com/asmsh/sema)
[](https://goreportcard.com/report/github.com/asmsh/sema)
[](https://github.com/asmsh/sema/actions)
[](https://raw.githack.com/wiki/asmsh/sema/coverage.html)
It bridges the gap between using channels, [sync.WaitGroup](https://pkg.go.dev/sync#WaitGroup) and [semaphore.Weighted](https://pkg.go.dev/golang.org/x/sync/semaphore#Weighted) for
managing concurrency.
### Features
* Fast, low-overhead implementation with performance comparable to `sync.WaitGroup` and `semaphore.Weighted`.
* Enables `select`-based waiting, in addition to standard `sync.WaitGroup` wait behavior.
* Exposes the internal counters in a concurrent-safe way.
* Clear API that's compatible with `sync.WaitGroup` and `semaphore.Weighted` with minimal changes.
* Usable zero value that's comparable to a `sync.WaitGroup`.
### Notes
* It wakes up blocked calls in random order, unlike `semaphore.Weighted` which preserves the order of calls.
* It's more suitable as a replacement for `semaphore.Weighted` when all the weights are of equal size.
* It relies on a single channel for blocking and wake-ups, so the Go runtime guarantees no starvation.
### Examples
#### Using it as a `sync.WaitGroup`:
```go
func example() {
var sg sema.Group
for i := 0; i < 10; i++ {
sg.Reserve()
go func(i int) {
defer sg.Free()
// Do some work...
}(i)
}
sg.Wait()
}
```
#### Using `select`-based waiting:
```go
func example() {
var sg sema.Group
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
for i := 0; i < 10; i++ {
sg.Reserve()
go func(i int) {
defer sg.Free()
// Do some work using the ctx...
ctx = ctx
}(i)
}
waitChan := sg.WaitChan()
select {
case <-waitChan:
// all the work is done.
case <-ctx.Done():
// the work times out.
}
}
```
#### Using it to limit concurrency (instead of a `semaphore.Weighted` or a channel):
```go
func example() {
var sg sema.Group
sg.SetSize(10)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
for i := 0; i < 10; i++ {
sg.ReserveN(ctx.Done(), 5)
go func(i int) {
defer sg.FreeN(5)
// Do some work...
log.Println("some work...")
}(i)
}
sg.Wait()
}
```