A Tour of Go の sync.Mutexの例をMutex使わないとどうなるか

　目下、職場社内の勉強会でGolangを採り上げており、A Tour of Go を参加者のみんなでざっと追いかけた。その途上で、ふと疑問に思ってちょろっと手を動かしてみたメモ。

　[sync.Mutex](https://golang.org/pkg/sync/#Mutex) のコード例 [mutex-counter.go](https://go-tour-jp.appspot.com/concurrency/9)  で、
「同じことを、Mutexを使わないと失敗するというコードも書いて欲しいなあ」
と思ったんです。
　というのも、Rubyの`Mutex`でも、Javaの`synchronized`でも、言語の基本を説明している書籍やサイトでは、たいてい
「排他制御を行わないと、ほら、ダメでしょ？」
という例があったりするもの。
　(たとえば [パーフェクトRuby 改訂2版](http://gihyo.jp/book/2017/978-4-7741-8977-2)でいえば、P.216 リスト 5.5 「複数のスレッドからcountupメソッドを呼び出す」みたいなやつ)
　なので、[mutex-counter.go](https://go-tour-jp.appspot.com/concurrency/9) から`mux`をコメントアウトして、以下のように`UnSafeCounter`にしてみました。

```go:no-mutex-counter.go

package main

import (
	"fmt"
//	"sync"
	"time"
)

// UnSafeCounter is NOT safe to use concurrently.
type UnSafeCounter struct {
	v   map[string]int
	// mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *UnSafeCounter) 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 *UnSafeCounter) 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 := UnSafeCounter{v: make(map[string]int)}
	for i := 0; i < 1000; i++ {
		go c.Inc("somekey")
	}

	time.Sleep(time.Second)
	fmt.Println(c.Value("somekey"))
}
```

で、これを`go run`してみると、冒頭に

```
fatal error: concurrent map writes
```
と表示され、その後だーっと例外トレースが表示されたのちエラー終了した。

　つらつらと調べていくと、Go 1.6 から、`map`を同時に複数のgoroutineから読み書きしようとすると、プログラムがクラッシュするようになったそうだ。

以下、https://golang.org/doc/go1.6#runtime より引用
> The runtime has added lightweight, best-effort detection of concurrent misuse of maps. As always, if one goroutine is writing to a map, no other goroutine should be reading or writing the map concurrently. If the runtime detects this condition, it prints a diagnosis and crashes the program. The best way to find out more about the problem is to run the program under the race detector, which will more reliably identify the race and give more detail.


うん、分かった。
気をつけるよ。


#### 補足

JAVAから離れて10年以上経って忘れたけど、JAVAのMap実装の`HashMap`はどうだったかAPIドキュメントをチラ見すると、

https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html

>Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:
>
   Map m = Collections.synchronizedMap(new HashMap(...));


ということで、「`HashMap` は `not synchronized` なので、複数のスレッドからひとつのMapへのアクセスがあり得る場合は、`Collections.synchronizedMap()` でラップしてあげてね」とのことだった。