2017-02-03 4 views
2

なぜ検出器は、次のコード文句golangレース:私は-raceで、私は次の警告を取得しています上記のコードを実行するとデータレース値によって渡された構造体の値からフィールドを読み取るときに

package main 

import (
    "fmt" 
    "sync" 
) 

type Counter struct { 
    value int 
    mtx  *sync.Mutex 
} 

func NewCounter() *Counter { 
    return &Counter {0, &sync.Mutex{}} 
} 

func (c *Counter) inc() { 
    c.mtx.Lock() 
    c.value++ 
    c.mtx.Unlock() 
} 

func (c Counter) get() int { 
    c.mtx.Lock() 
    res := c.value 
    c.mtx.Unlock() 
    return res 
} 

func main() { 
    var wg sync.WaitGroup 
    counter := NewCounter() 
    max := 100 
    wg.Add(max) 

    // consumer 
    go func() { 
     for i := 0; i < max ; i++ { 
      value := counter.get() 
      fmt.Printf("counter value = %d\n", value) 
      wg.Done() 
     } 
    }() 
    // producer 
    go func() { 
     for i := 0; i < max ; i++ { 
      counter.inc() 
     } 
    }() 

    wg.Wait() 
} 

================== 
WARNING: DATA RACE 
Read at 0x00c0420042b0 by goroutine 6: 
    main.main.func1() 
     main.go:39 +0x72 

Previous write at 0x00c0420042b0 by goroutine 7: 
    main.(*Counter).inc() 
     main.go:19 +0x8b 
    main.main.func2() 
     main.go:47 +0x50 

Goroutine 6 (running) created at: 
    main.main() 
     main.go:43 +0x167 

Goroutine 7 (running) created at: 
    main.main() 
     main.go:49 +0x192 
================== 

func (c Counter) get() intfunc (c *Counter) get() intに変更すると、すべて正常に動作しています。 get()の受信機タイプはポインタでなければなりません。私はなぜそれが混乱しているのですか?私は "-copylocks"を認識していますが、この場合、mtxは値ではなくポインタです。

main.go:23:値を通過するが、ロックを取得:main.Counterは

sync.Mutex`が含まれて私は 'MTX' を変更した場合、私はこの警告を取得 vet -copylocksと値と実行プログラムを可能にします

それは理にかなっています。

注:この質問は、スレッドセーフカウンタ

link to playground code

答えて

2

レースがあるためget()メソッドの価値受信機のあるを実装する方法に関するものではありません。 get()メソッドを呼び出すには、構造体のコピーをメソッド式に渡す必要があります。シンタックスシュガーなしのメソッド呼び出しは、次のようになります。構造体のコピー

value := Counter.get(*counter) 

はレースは、メソッド呼び出しの行に報告されている理由であるメソッドがロックを取ることができます前に発生valueフィールドを、読ん伴い、その方法ではなくむしろ。

このため、受信機をポインタ受信機に変更すると、問題が解決されます。また、すべての受信者がポインタである必要があるため、はsync.Mutexの値のままにすることができるため、初期化する必要はありません。

1

@JimB任意ロッキングなしで 同じ変数がinc()に変異されているので、フィールド値が最初に読み出され、その後、コピーされた場合 にコピーを渡されget()方法の場合には、指摘しているように、レースが検出されます。

は、さらにまた、あなたはもはや今 ポインタのみがコピーされ、レースではなく根本的な価値を見てはならない。その場合には、ポインタすなわちvalue *intにフィールドvalue の種類を変更することができ、この点を説明します。つまり、 の意図を明確にするために、受信者タイプget()をポインタに変更するとよりクリーンです。ここで

は同じ周りに良いのwikiです - https://github.com/golang/go/wiki/CodeReviewComments#receiver-type

方法についての簡単な解説: https://golang.org/ref/spec#Method_values

関連する問題