]> Cypherpunks repositories - gostls13.git/commitdiff
doc/effective_go: fix server example that shares var between goroutines
authorRob Pike <r@golang.org>
Wed, 2 Oct 2013 18:35:25 +0000 (11:35 -0700)
committerRob Pike <r@golang.org>
Wed, 2 Oct 2013 18:35:25 +0000 (11:35 -0700)
Use it as a teaching example about how to solve this problem.

Fixes #6501

R=golang-dev, adg, rsc
CC=golang-dev
https://golang.org/cl/14250043

doc/effective_go.html

index 35b15e8df50eedbee12338dfe4a91f97027a6c8e..6e7ee1a28e534ea24eb675c8b32acf852c4a9d37 100644 (file)
@@ -2981,12 +2981,53 @@ of them can run at any moment.
 As a result, the program can consume unlimited resources if the requests come in too fast.
 We can address that deficiency by changing <code>Serve</code> to
 gate the creation of the goroutines.
+Here's an obvious solution, but beware it has a bug we'll fix subsequently:
 </p>
 
 <pre>
 func Serve(queue chan *Request) {
     for req := range queue {
         &lt;-sem
+        go func() {
+            process(req) // Buggy; see explanation below.
+            sem &lt;- 1
+        }()
+    }
+}</pre>
+
+<p>
+The bug is that in a Go <code>for</code> loop, the loop variable
+is reused for each iteration, so the <code>req</code>
+variable is shared across all goroutines.
+That's not what we want.
+We need to make sure that <code>req</code> is unique for each goroutine.
+Here's one way to do that, passing the value of <code>req</code> as an argument
+to the closure in the goroutine:
+</p>
+
+<pre>
+func Serve(queue chan *Request) {
+    for req := range queue {
+        &lt;-sem
+        go func(req *Request) {
+            process(req)
+            sem &lt;- 1
+        }(req)
+    }
+}</pre>
+
+<p>
+Compare this version with the previous to see the difference in how
+the closure is declared and run.
+Another solution is just to create a new variable with the same
+name, as in this example:
+</p>
+
+<pre>
+func Serve(queue chan *Request) {
+    for req := range queue {
+        &lt;-sem
+        req := req // Create new instance of req for the goroutine.
         go func() {
             process(req)
             sem &lt;- 1
@@ -2995,7 +3036,22 @@ func Serve(queue chan *Request) {
 }</pre>
 
 <p>
-Another solution that manages resources well is to start a fixed
+It may seem odd to write
+</p>
+
+<pre>
+req := req
+</pre>
+
+<p>
+but it's a legal and idiomatic in Go to do this.
+You get a fresh version of the variable with the same name, deliberately
+shadowing the loop variable locally but unique to each goroutine.
+</p>
+
+<p>
+Going back to the general problem of writing the server,
+another approach that manages resources well is to start a fixed
 number of <code>handle</code> goroutines all reading from the request
 channel.
 The number of goroutines limits the number of simultaneous