Last active
February 27, 2024 13:30
-
-
Save driquet/6d34d5fbb0255ea1f738b90641796b8e to your computer and use it in GitHub Desktop.
Throttling in Golang with Semaphore Magic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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