12

次のように、マップに同時にアクセスできるプログラムがあるとします。

func getKey(r *http.Request) string { ... }

values := make(map[string]int)

http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
  key := getKey(r)
  fmt.Fprint(w, values[key])
})

http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
  key := getKey(r)
  values[key] = rand.Int()
})

マップの書き込みは非アトミックであるため、これは悪いことです。したがって、読み取り/書き込みミューテックスを使用できます

func getKey(r *http.Request) string { ... }

values := make(map[string]int)
var lock sync.RWMutex

http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
  key := getKey(r)
  lock.RLock()
  fmt.Fprint(w, values[key])
  lock.RUnlock()
})

http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
  key := getKey(r)
  lock.Lock()
  values[key] = rand.Int()
  lock.Unlock()
})

チャネルではなくミューテックスを直接使用しているという事実を除けば、これは問題ないようです。

これを実装するより慣用的な方法は何ですか? それとも、これは本当にミューテックスだけで十分な時代の 1 つですか?

4

4 に答える 4

13

このラーグレーは、パフォーマンスへの期待と、このマップが最終的にどのように使用されるかによって決まると思います。

この同じ質問を調査していたとき、あなたの質問に答えるはずのこの非常に役立つ記事に出くわしました。

私の個人的な回答は、ミューテックスを使用する必要が本当にない限り、デフォルトでチャネルを使用する必要があるということです。慣用的な Go の中心点のようなものは、ミューテックスを使用する必要がなく、より高レベルのチャネル機能に固執する場合にロックを心配する必要がないことです。Go のモットーを思い出してください。

もう 1 つ、Mark Summerfield のGo bookには、同時使用のための安全なマップを構築するためのさまざまな手法の非常に詳細なツアーがあります。

Go の作成者の 1 人であるRob Pike のスライドを強調するには、次のようにします。

同時実行性による同期の簡素化

  • 明示的な同期は必要ありません
  • プログラムの構造は暗黙的に同期されています

ミューテックスのようなプリミティブを使用する道をたどると、プログラムがより複雑になるため、これを正しく行うのは非常に困難です。あなたは警告されました。

また、Golang サイト自体からの引用は次のとおりです。

多くの環境での同時プログラミングは、共有変数への正しいアクセスを実装するために必要な微妙な作業によって困難になります。Go は、共有された値がチャネル上で渡され、実際には実行の個別のスレッドによって積極的に共有されることのない、別のアプローチを推奨しています。常に 1 つのゴルーチンだけが値にアクセスできます。このアプローチは行き過ぎてしまう可能性があります。たとえば、参照カウントは、整数変数の周りにミューテックスを配置することで最適に実行できます。ただし、高レベルのアプローチとして、チャネルを使用してアクセスを制御すると、明確で正しいプログラムを簡単に作成できます。

于 2013-08-12T16:43:46.490 に答える
3

チャネルを相互排除のメカニズムとして使用する、代替のチャネルベースのアプローチを次に示します。

func getKey(r *http.Request) string { ... }

values_ch := make(chan map[string]int, 1)
values_ch <- make(map[string]int)

http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
  key := getKey(r)
  values := <- values_ch
  fmt.Fprint(w, values[key])
  values_ch <- values
})

http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
  key := getKey(r)
  values := <- values_ch
  values[key] = rand.Int()
  values_ch <- values
})

ここで、最初にリソースを共有チャネルに配置しました。その後、ゴルーチンはその共有リソースを借りて返すことができます。ただし、 を使用したソリューションとは異なりRWMutex、複数のリーダーが互いにブロックする可能性があります。

于 2013-08-13T02:25:18.820 に答える