Skip to content

Instantly share code, notes, and snippets.

@driquet
Last active February 27, 2024 13:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save driquet/6d34d5fbb0255ea1f738b90641796b8e to your computer and use it in GitHub Desktop.
Save driquet/6d34d5fbb0255ea1f738b90641796b8e to your computer and use it in GitHub Desktop.
Throttling in Golang with Semaphore Magic
// This code is licensed under the terms of the MIT license
// Source: https://driquet.info/throttling-semaphore/
package main
type token struct{}
// Semaphore represents a structure able to limit simultaneous access to a shared resource.
type Semaphore struct {
c chan token
}
// NewSemaphore creates a new semaphore that allows size simultaneous operations.
func NewSemaphore(size int) *Semaphore {
s := &Semaphore{
c: make(chan token, size),
}
// Fill the channel with tokens
for i := 0; i < size; i++ {
s.c <- token{}
}
return s
}
// TearDown tears down a semaphore.
// If there is any ongoing operation, this will wait for them.
func (s *Semaphore) TearDown() {
for i := 0; i < cap(s.c); i++ {
<-s.c
}
}
// Acquire acquires a semaphore token, blocking until resources are available.
func (s *Semaphore) Acquire() {
<-s.c
}
// Release releases a semaphore token.
func (s *Semaphore) Release() {
s.c <- token{}
}
// TryAcquire tries to acquire a semaphore token, without blocking.
// On success, returns true. On failure, returns false and leaves the semaphore unchanged.
func (s *Semaphore) TryAcquire() bool {
select {
case <-s.c:
return true
default:
return false
}
}
func main() {
maxWorkers := 10
out := make([]uint64, 32)
// Creating the semaphore
sem := NewSemaphore(maxWorkers)
// Launch the factorial operation using up to maxWorkers goroutines at a time.
for i := range out {
// Acquire a semaphore token (or wait for one available)
sem.Acquire()
go func(i int) {
out[i] = factorial(i)
// Release a semaphore token
sem.Release()
}(i)
}
// Wait for the workers to finish their job
sem.TearDown()
fmt.Println(out)
}
func factorial(n int) uint64 {
if n > 0 {
return uint64(n) * factorial(n-1)
}
return 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment