From b1ef4998210b0d692bda73589c3e7478c428c29c Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Fri, 10 Sep 2021 06:11:56 -0700
Subject: [PATCH] Rewrite the WaitGroup example to be more idiomatic
A wrapper closure invokes wg.Done
Still mention the pass-by-pointer requirements on the WaitGroup
Fixes #278
---
examples/waitgroups/waitgroups.go | 26 +++++++---
examples/waitgroups/waitgroups.hash | 4 +-
public/waitgroups | 75 +++++++++++++++++------------
3 files changed, 63 insertions(+), 42 deletions(-)
diff --git a/examples/waitgroups/waitgroups.go b/examples/waitgroups/waitgroups.go
index 9e045d4..c32cd24 100644
--- a/examples/waitgroups/waitgroups.go
+++ b/examples/waitgroups/waitgroups.go
@@ -10,12 +10,7 @@ import (
)
// This is the function we'll run in every goroutine.
-// Note that a WaitGroup must be passed to functions by
-// pointer.
-func worker(id int, wg *sync.WaitGroup) {
- // On return, notify the WaitGroup that we're done.
- defer wg.Done()
-
+func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
// Sleep to simulate an expensive task.
@@ -26,14 +21,29 @@ func worker(id int, wg *sync.WaitGroup) {
func main() {
// This WaitGroup is used to wait for all the
- // goroutines launched here to finish.
+ // goroutines launched here to finish. Note: if a WaitGroup is
+ // explicitly passed into functions, it should be done *by pointer*.
+ // This would be important if, for example, our worker had to launch
+ // additional goroutines.
var wg sync.WaitGroup
// Launch several goroutines and increment the WaitGroup
// counter for each.
for i := 1; i <= 5; i++ {
wg.Add(1)
- go worker(i, &wg)
+ // Avoid re-use of the same `i` value in each goroutine closure.
+ // See [the FAQ](https://golang.org/doc/faq#closures_and_goroutines)
+ // for more details.
+ i := i
+
+ // Wrap the worker call in a closure that makes sure to tell
+ // the WaitGroup that this worker is done. This way the worker
+ // itself does not have to be aware of the concurrency primitives
+ // involved in its execution.
+ go func() {
+ defer wg.Done()
+ worker(i)
+ }()
}
// Block until the WaitGroup counter goes back to 0;
diff --git a/examples/waitgroups/waitgroups.hash b/examples/waitgroups/waitgroups.hash
index 2de67d2..9dea057 100644
--- a/examples/waitgroups/waitgroups.hash
+++ b/examples/waitgroups/waitgroups.hash
@@ -1,2 +1,2 @@
-b87ababcf7e1ce54107252c658840097bb6060a7
-vXBl8zQpDYj
+58031ceb701a1cab27498efd89adadbf1ea6b3e6
+vmjCBfN6MJE
diff --git a/public/waitgroups b/public/waitgroups
index b837983..d1b609a 100644
--- a/public/waitgroups
+++ b/public/waitgroups
@@ -42,7 +42,7 @@ use a wait group.
- 
+ 
package main
|
@@ -65,39 +65,14 @@ use a wait group.
- This is the function we’ll run in every goroutine.
-Note that a WaitGroup must be passed to functions by
-pointer.
+ This is the function we’ll run in every goroutine.
|
-func worker(id int, wg *sync.WaitGroup) {
-
- |
-
-
-
-
- On return, notify the WaitGroup that we’re done.
-
- |
-
-
-
- defer wg.Done()
-
- |
-
-
-
-
-
- |
-
-
- fmt.Printf("Worker %d starting\n", id)
+func worker(id int) {
+ fmt.Printf("Worker %d starting\n", id)
|
@@ -131,7 +106,10 @@ pointer.
This WaitGroup is used to wait for all the
-goroutines launched here to finish.
+goroutines launched here to finish. Note: if a WaitGroup is
+explicitly passed into functions, it should be done by pointer.
+This would be important if, for example, our worker had to launch
+additional goroutines.
|
@@ -153,7 +131,40 @@ counter for each.
for i := 1; i <= 5; i++ {
wg.Add(1)
- go worker(i, &wg)
+
+ |
+
+
+
+
+ Avoid re-use of the same i value in each goroutine closure.
+See the FAQ
+for more details.
+
+ |
+
+
+
+ i := i
+
+ |
+
+
+
+
+ Wrap the worker call in a closure that makes sure to tell
+the WaitGroup that this worker is done. This way the worker
+itself does not have to be aware of the concurrency primitives
+involved in its execution.
+
+ |
+
+
+
+ go func() {
+ defer wg.Done()
+ worker(i)
+ }()
}
|
@@ -240,7 +251,7 @@ is likely to be different for each invocation.