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
This commit is contained in:
Eli Bendersky 2021-09-10 06:11:56 -07:00
parent 4de485a514
commit b1ef499821
3 changed files with 63 additions and 42 deletions

View File

@ -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;

View File

@ -1,2 +1,2 @@
b87ababcf7e1ce54107252c658840097bb6060a7
vXBl8zQpDYj
58031ceb701a1cab27498efd89adadbf1ea6b3e6
vmjCBfN6MJE

75
public/waitgroups generated
View File

@ -42,7 +42,7 @@ use a <em>wait group</em>.</p>
</td>
<td class="code leading">
<a href="http://play.golang.org/p/vXBl8zQpDYj"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<a href="http://play.golang.org/p/vmjCBfN6MJE"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<pre class="chroma"><span class="kn">package</span> <span class="nx">main</span>
</pre>
</td>
@ -65,39 +65,14 @@ use a <em>wait group</em>.</p>
<tr>
<td class="docs">
<p>This is the function we&rsquo;ll run in every goroutine.
Note that a WaitGroup must be passed to functions by
pointer.</p>
<p>This is the function we&rsquo;ll run in every goroutine.</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="kd">func</span> <span class="nf">worker</span><span class="p">(</span><span class="nx">id</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">wg</span> <span class="o">*</span><span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">)</span> <span class="p">{</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>On return, notify the WaitGroup that we&rsquo;re done.</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<pre class="chroma"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Worker %d starting\n&#34;</span><span class="p">,</span> <span class="nx">id</span><span class="p">)</span>
<span class="kd">func</span> <span class="nf">worker</span><span class="p">(</span><span class="nx">id</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Worker %d starting\n&#34;</span><span class="p">,</span> <span class="nx">id</span><span class="p">)</span>
</pre>
</td>
</tr>
@ -131,7 +106,10 @@ pointer.</p>
<tr>
<td class="docs">
<p>This WaitGroup is used to wait for all the
goroutines launched here to finish.</p>
goroutines launched here to finish. Note: if a WaitGroup is
explicitly passed into functions, it should be done <em>by pointer</em>.
This would be important if, for example, our worker had to launch
additional goroutines.</p>
</td>
<td class="code leading">
@ -153,7 +131,40 @@ counter for each.</p>
<pre class="chroma">
<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;=</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
<span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">go</span> <span class="nf">worker</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">wg</span><span class="p">)</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>Avoid re-use of the same <code>i</code> value in each goroutine closure.
See <a href="https://golang.org/doc/faq#closures_and_goroutines">the FAQ</a>
for more details.</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="nx">i</span> <span class="o">:=</span> <span class="nx">i</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>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.</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
<span class="k">defer</span> <span class="nx">wg</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
<span class="nf">worker</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
<span class="p">}()</span>
<span class="p">}</span>
</pre>
</td>
@ -240,7 +251,7 @@ is likely to be different for each invocation.</p>
</div>
<script>
var codeLines = [];
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import (\u000A \"fmt\"\u000A \"sync\"\u000A \"time\"\u000A)\u000A');codeLines.push('func worker(id int, wg *sync.WaitGroup) {\u000A');codeLines.push(' defer wg.Done()\u000A');codeLines.push(' fmt.Printf(\"Worker %d starting\\n\", id)\u000A');codeLines.push(' time.Sleep(time.Second)\u000A fmt.Printf(\"Worker %d done\\n\", id)\u000A}\u000A');codeLines.push('func main() {\u000A');codeLines.push(' var wg sync.WaitGroup\u000A');codeLines.push(' for i :\u003D 1; i \u003C\u003D 5; i++ {\u000A wg.Add(1)\u000A go worker(i, \u0026wg)\u000A }\u000A');codeLines.push(' wg.Wait()\u000A');codeLines.push('}\u000A');codeLines.push('');codeLines.push('');
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import (\u000A \"fmt\"\u000A \"sync\"\u000A \"time\"\u000A)\u000A');codeLines.push('func worker(id int) {\u000A fmt.Printf(\"Worker %d starting\\n\", id)\u000A');codeLines.push(' time.Sleep(time.Second)\u000A fmt.Printf(\"Worker %d done\\n\", id)\u000A}\u000A');codeLines.push('func main() {\u000A');codeLines.push(' var wg sync.WaitGroup\u000A');codeLines.push(' for i :\u003D 1; i \u003C\u003D 5; i++ {\u000A wg.Add(1)\u000A');codeLines.push(' i :\u003D i\u000A');codeLines.push(' go func() {\u000A defer wg.Done()\u000A worker(i)\u000A }()\u000A }\u000A');codeLines.push(' wg.Wait()\u000A');codeLines.push('}\u000A');codeLines.push('');codeLines.push('');
</script>
<script src="site.js" async></script>
</body>