Буферизованный канал — простейший семафор. Размер буфера = макс. число параллельных операций.
type Semaphore chan struct{}
func NewSemaphore(max int) Semaphore {
return make(Semaphore, max)
}
func (s Semaphore) Acquire() { s <- struct{}{} }
func (s Semaphore) Release() { <-s }
// Использование: макс 3 параллельных HTTP-запроса
sem := NewSemaphore(3)
for _, url := range urls {
sem.Acquire()
go func(u string) {
defer sem.Release()
resp, _ := http.Get(u)
resp.Body.Close()
}(url)
}
С context (отменяемый acquire):
func (s Semaphore) AcquireCtx(ctx context.Context) error {
select {
case s <- struct{}{}:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
Для production есть golang.org/x/sync/semaphore с weighted семафором (одна горутина может занять несколько слотов).