LoginSignup
9
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-11-23

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

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

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ドキュメントをチラ見すると、

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(...));

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

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2