Skip to content

Instantly share code, notes, and snippets.

@besser
Last active October 9, 2020 22:14
Show Gist options
  • Save besser/986a3e13759082e5620d1863b4b40480 to your computer and use it in GitHub Desktop.
Save besser/986a3e13759082e5620d1863b4b40480 to your computer and use it in GitHub Desktop.
Semaphore pattern example
package main
import (
"context"
"fmt"
"log"
"time"
"golang.org/x/sync/semaphore"
)
func main() {
ctx := context.TODO()
var (
maxWorkers = 1000
routines = 1000000
sem = semaphore.NewWeighted(int64(maxWorkers))
)
fmt.Println("Starting...")
for i := 0; i < routines; i++ {
if err := sem.Acquire(ctx, 1); err != nil {
log.Printf("Failed to acquire semaphore: %v", err)
break
}
go func(n int) {
defer sem.Release(1)
routine(n)
}(i)
}
if err := sem.Acquire(ctx, int64(maxWorkers)); err != nil {
log.Printf("Failed to acquire semaphore: %v", err)
}
fmt.Println("Done!")
}
func routine(n int) {
fmt.Printf("go routine %d\n", n)
// take a time
time.Sleep(100 * time.Millisecond)
}
package main
import (
"context"
"errors"
"fmt"
"log"
"math/rand"
"time"
"golang.org/x/sync/semaphore"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Make sure it's called to release resources even if no errors
var (
maxWorkers = 1000
routines = 1000000
sem = semaphore.NewWeighted(int64(maxWorkers))
)
errs := make(chan error, 2) // Buffer for 2 errors
fmt.Println("Starting...")
for i := 0; i < routines; i++ {
if err := sem.Acquire(ctx, 1); err != nil {
log.Printf("Failed to acquire semaphore: %v", err)
break
}
go func(n int) {
defer sem.Release(1)
// Check if any error occurred in any other gorouties:
select {
case <-ctx.Done():
return // Error somewhere, terminate
default: // Default is must to avoid blocking
}
err := routine(n)
if err != nil {
errs <- fmt.Errorf("Worker #%d, error: %v\n", i, err)
cancel()
return
}
}(i)
}
if err := sem.Acquire(ctx, int64(maxWorkers)); err != nil {
log.Printf("Failed to acquire semaphore: %v", err)
}
// Return error, if any:
if ctx.Err() != nil {
fmt.Printf("Finished with error: %v", <-errs)
} else {
fmt.Println("Finished ok!")
}
fmt.Println("Done!")
}
func routine(n int) error {
fmt.Printf("go routine %d\n", n)
if rand.Intn(100) < 10 { // 10% of failure
return errors.New("random error")
}
// take a time
time.Sleep(100 * time.Millisecond)
return nil
}
package main
import (
"fmt"
"time"
)
type empty struct{}
type semaphore chan empty
func main() {
maxWorkers := 1000
routines := 1000000
sem := make(semaphore, maxWorkers)
e := empty{}
fmt.Println("Starting...")
for i := 0; i < routines; i++ {
sem <- e
go func(n int) {
defer func() {
<-sem
}()
routine(n)
}(i)
}
sem.P()
fmt.Println("Done!")
}
func routine(n int) {
fmt.Printf("go routine %d\n", n)
// take a time
time.Sleep(100 * time.Millisecond)
}
// fill resources
func (s semaphore) P() {
e := empty{}
for i := 0; i < cap(s); i++ {
s <- e
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment