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.