-
Notifications
You must be signed in to change notification settings - Fork 156
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
I've commented several times in various forums that basically every time I've seen the “worker goroutine” pattern in Go, there has turned out to be a cleaner implementation using semaphores. This change adds a simple such example. (For more complex usage, I would generally pair the semaphore with an errgroup.Group.) Change-Id: Ibf69ee761d14ba59c1acc6a2d595b4fcf0d8f6d6 Reviewed-on: https://go-review.googlesource.com/75170 Reviewed-by: Ross Light <[email protected]>
- Loading branch information
Bryan C. Mills
committed
Nov 1, 2017
1 parent
8e0aa68
commit fd80eb9
Showing
3 changed files
with
98 additions
and
12 deletions.
There are no files selected for viewing
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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Copyright 2017 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package semaphore_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"runtime" | ||
|
||
"golang.org/x/sync/semaphore" | ||
) | ||
|
||
// Example_workerPool demonstrates how to use a semaphore to limit the number of | ||
// goroutines working on parallel tasks. | ||
// | ||
// This use of a semaphore mimics a typical “worker pool” pattern, but without | ||
// the need to explicitly shut down idle workers when the work is done. | ||
func Example_workerPool() { | ||
ctx := context.TODO() | ||
|
||
var ( | ||
maxWorkers = runtime.GOMAXPROCS(0) | ||
sem = semaphore.NewWeighted(int64(maxWorkers)) | ||
out = make([]int, 32) | ||
) | ||
|
||
// Compute the output using up to maxWorkers goroutines at a time. | ||
for i := range out { | ||
// When maxWorkers goroutines are in flight, Acquire blocks until one of the | ||
// workers finishes. | ||
if err := sem.Acquire(ctx, 1); err != nil { | ||
log.Printf("Failed to acquire semaphore: %v", err) | ||
break | ||
} | ||
|
||
go func(i int) { | ||
defer sem.Release(1) | ||
out[i] = collatzSteps(i + 1) | ||
}(i) | ||
} | ||
|
||
// Acquire all of the tokens to wait for any remaining workers to finish. | ||
// | ||
// If you are already waiting for the workers by some other means (such as an | ||
// errgroup.Group), you can omit this final Acquire call. | ||
if err := sem.Acquire(ctx, int64(maxWorkers)); err != nil { | ||
log.Printf("Failed to acquire semaphore: %v", err) | ||
} | ||
|
||
fmt.Println(out) | ||
|
||
// Output: | ||
// [0 1 7 2 5 8 16 3 19 6 14 9 9 17 17 4 12 20 20 7 7 15 15 10 23 10 111 18 18 18 106 5] | ||
} | ||
|
||
// collatzSteps computes the number of steps to reach 1 under the Collatz | ||
// conjecture. (See https://en.wikipedia.org/wiki/Collatz_conjecture.) | ||
func collatzSteps(n int) (steps int) { | ||
if n <= 0 { | ||
panic("nonpositive input") | ||
} | ||
|
||
for ; n > 1; steps++ { | ||
if steps < 0 { | ||
panic("too many steps") | ||
} | ||
|
||
if n%2 == 0 { | ||
n /= 2 | ||
continue | ||
} | ||
|
||
const maxInt = int(^uint(0) >> 1) | ||
if n > (maxInt-1)/3 { | ||
panic("overflow") | ||
} | ||
n = 3*n + 1 | ||
} | ||
|
||
return steps | ||
} |
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