tour: add explanation of sync.Mutex
sync.Mutex is needed to solve the last exercise of the tour
Fixes golang/go#13110
Change-Id: I0b10f8c0fc0e3c656377b93bdbf40e38c5cb762b
Reviewed-on: https://go-review.googlesource.com/17403
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/content/concurrency.article b/content/concurrency.article
index 8dd1308..9bb55c0 100644
--- a/content/concurrency.article
+++ b/content/concurrency.article
@@ -130,12 +130,37 @@
.play concurrency/exercise-equivalent-binary-trees.go
+* sync.Mutex
+
+We've seen how channels are great for communication among goroutines.
+
+But what if we don't need communication? What if we just want to make sure only
+one goroutine can access a variable at a time to avoid conflicts?
+
+This concept is called _mutual_exclusion_, and the conventional name for the data structure that provides it is _mutex_.
+
+Go's standard library provides mutual exclusion with
+[[https://golang.org/pkg/sync/#Mutex][`sync.Mutex`]] and its two methods:
+
+- `Lock`
+- `Unlock`
+
+We can define a block of code to be executed in mutual exclusion by surrounding it
+with a call to `Lock` and `Unlock` as shown on the `Inc` method.
+
+We can also use `defer` to ensure the mutex will be unlocked as in the `Value` method.
+
+.play concurrency/mutex-counter.go
+
* Exercise: Web Crawler
In this exercise you'll use Go's concurrency features to parallelize a web crawler.
Modify the `Crawl` function to fetch URLs in parallel without fetching the same URL twice.
+_Hint_: you can keep a cache of the URLs that have been fetched on a map, but maps alone are not
+safe for concurrent use!
+
.play concurrency/exercise-web-crawler.go
* Where to Go from here...
diff --git a/content/concurrency/mutex-counter.go b/content/concurrency/mutex-counter.go
new file mode 100644
index 0000000..ed727bd
--- /dev/null
+++ b/content/concurrency/mutex-counter.go
@@ -0,0 +1,41 @@
+// +build OMIT
+
+package main
+
+import (
+ "fmt"
+ "sync"
+ "time"
+)
+
+// SafeCounter is safe to use concurrently.
+type SafeCounter struct {
+ v map[string]int
+ mux sync.Mutex
+}
+
+// Inc increments the counter for the given key.
+func (c *SafeCounter) Inc(key string) {
+ c.mux.Lock()
+ // Lock so only one goroutine at a time can access the map c.v.
+ c.v[key]++
+ c.mux.Unlock()
+}
+
+// Value returns the current value of the counter for the given key.
+func (c *SafeCounter) Value(key string) int {
+ c.mux.Lock()
+ // Lock so only one goroutine at a time can access the map c.v.
+ defer c.mux.Unlock()
+ return c.v[key]
+}
+
+func main() {
+ c := SafeCounter{v: make(map[string]int)}
+ for i := 0; i < 1000; i++ {
+ go c.Inc("somekey")
+ }
+
+ time.Sleep(time.Second)
+ fmt.Println(c.Value("somekey"))
+}