It bridges the gap between using channels, sync.WaitGroup and semaphore.Weighted for managing concurrency.
- Fast, low-overhead implementation with performance comparable to
sync.WaitGroupandsemaphore.Weighted. - Enables
select-based waiting, in addition to standardsync.WaitGroupwait behavior. - Exposes the internal counters in a concurrent-safe way.
- Clear API that's compatible with
sync.WaitGroupandsemaphore.Weightedwith minimal changes. - Usable zero value that's comparable to a
sync.WaitGroup.
- It wakes up blocked calls in random order, unlike
semaphore.Weightedwhich preserves the order of calls. - It's more suitable as a replacement for
semaphore.Weightedwhen 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.
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()
}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.
}
}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()
}