2012-05-23 16 views
12

この質問に対する彼の答え:Golang for Windows erratic behavior? user @distributed同時のゴルーチンの共有変数へのアクセスをロック/同期することをお勧めします。同時ゴルーチン中にGoの変数へのアクセスをロック/同期する方法は?

どうすればいいですか?

問題について

より:

私はこのコード(views上の閉鎖と返される関数)同時に複数のゴルーチン上で実行されているを取得する:

func makeHomeHandler() func(c *http.Conn, r *http.Request) { 
    views := 1 
    return func(c *http.Conn, r *http.Request) { 
     fmt.Fprintf(c, "Counting %s, %d so far.", r.URL.Path[1:], views) 
     views++ 
    } 
} 

IO機能がかかるように見えますそれは時間だし、結果として、私はこのような出力を得る:

Counting monkeys, 5 so far. 
Counting monkeys, 5 so far. 
Counting monkeys, 5 so far. 
Counting monkeys, 8 so far. 
Counting monkeys, 8 so far. 
Counting monkeys, 8 so far. 
Counting monkeys, 11 so far. 

それは罰金インクリメントし、それを印刷しますときに私は運転印刷+ INCRそれを見ることができますエメンティングは原子ではありません。私はそれを変更した場合

func makeHomeHandler() func(c *http.Conn, r *http.Request) { 
    views := 0 
    return func(c *http.Conn, r *http.Request) { 
     views++ 
     // I can only hope that other goroutine does not increment the counter 
     // at this point, i.e., right after the previous line and before the 
     // next one are executed! 
     views_now := views 
     fmt.Fprintf(c, "Counting %s, %d so far.", r.URL.Path[1:], views_now) 
    } 
} 

正常に動作するようですが、私はそれが最終的に失敗することはありません場合は完全にはわからない...

+0

、[' expvar'パッケージ](https://golang.org/pkg/expvar/)も、あなたならば、別のオプションですHTTPを介して値を公開する必要があります(たとえば、リモートで実行されているサーバー上の値を照会するなど)。 –

答えて

19

同期カウンタがすべて必要な場合は、sync.Mutexを使用して標準的な解決策です。シンク/アトミックパッケージは、低レベルのものや深刻なパフォーマンスの問題を測定した場合にのみ使用してください。あなたの特定の問題については

type Counter struct { 
    mu sync.Mutex 
    x int64 
} 

func (c *Counter) Add(x int64) { 
    c.mu.Lock() 
    c.x += x 
    c.mu.Unlock() 
} 

func (c *Counter) Value() (x int64) { 
    c.mu.Lock() 
    x = c.x 
    c.mu.Unlock() 
    return 
} 

func makeHomeHandler() func(c http.ResponseWriter, r *http.Request) { 
    var views Counter 
    return func(w http.ResponseWriter, r *http.Request) { 
     fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], views.Value()) 
     views.Add(1) 
    } 
} 

、私はむしろ閉鎖を返すよりも、http.Handlerインタフェースを満たす新しいタイプを定義することをお勧めしたいです。それはあまりにも単純になります: `同期/ atomic`を言及するの回答に加えて

type homeHandler struct { 
    mu sync.Mutex 
    views int64 
} 

func (h *homeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    h.mu.Lock() 
    defer h.mu.Unlock() 
    fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], h.views) 
    h.views++ 
} 

func init() { 
    http.Handle("/", new(homeHandler)) 
} 
+2

"シンク/アトミックパッケージは、低レベルのものや深刻なパフォーマンスの問題を測定した場合にのみ使用してください。ちょうど興味があります、それはなぜですか? – ReyCharles

+4

これは、提供するプリミティブが使い方が間違っているため、必ずしも良いAPIではなく機械命令によく対応するように設計されています。この場合、質問者がページビュー以外の他の統計情報を格納するようにアップグレードしたい場合、atomic.AddUint64を使用するコードはこれに対応するように簡単に変更されません。つまり、sync.Mutexを使用するコードです。 – rog

10

syncパッケージには、いくつかの同期プリミティブを持っています。問題に応じて、RWMutexまたはプレーンなMutexを使用することができます。

もっと具体的な回答が必要な場合は、それが何のためにあるのか、さらに詳しい情報を提供してください。

編集:リンクされた質問を読んだら、おそらくsync/atomicを探していますが、Mutexも問題ありません。

Edit2:あなたの記事を例文で更新したのを見ました。シンク/アトミックを使用したコードは次のとおりです。

func makeHomeHandler() func(w http.ResponseWriter, r *http.Request) { 
    var views *uint64 = new(uint64) 
    atomic.StoreUint64(views, 0) // I don't think this is strictly necessary 
    return func(w http.ResponseWriter, r *http.Request) { 
     // Atomically add one to views and get the new value 
     // Perhaps you want to subtract one here 
     views_now := atomic.AddUint64(views, 1) 
     fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], views_now) 
    } 
} 

(注:タイプミス/ brainfartsがあるかもしれないので、私は上記のテストしていない) 私は今、それをテストしました。

+1

これは良い答えです。原子インクリメントは、複数の命令を必要とするミューテックスではなく、「LOCK ADD」命令を使用します。 –

関連する問題