GolangはMap同時読み書きセキュリティの問題を解決

9913 ワード

一、エラーケース

package main

import (
	"fmt"
	"time"
)

var TestMap map[string]string

func init() {
	TestMap = make(map[string]string, 1)
}

func main() {
	for i := 0; i < 1000; i++ {
		go Write("aaa")
		go Read("aaa")
		go Write("bbb")
		go Read("bbb")
	}
	time.Sleep(5 * time.Second)
}

func Read(key string) {
	fmt.Println(TestMap[key])
}

func Write(key string) {
	TestMap[key] = key
}

上のコードの実行確率にエラーが発生しました:fatal error:concurrent map writes

二、問題分析


ネット上ではgolangプログラミングにおけるmap同時読み書きに関する資料が多いが、同時読み書きが上記の誤りをもたらし、同時読み書きが原因なのか、この多くの資料は説明されていない.
私たちは上のケースをそれぞれループの中でReadとWrite関数の呼び出しを注釈し、それぞれ同時読み取りと同時書き込みをテストします.
サイクル数はそれぞれ100,1 w,100 w回テストされ,同時読み取り操作は絶対に上のエラーを報告しないが,同時書き込みは基本的にエラーを報告する.
したがって,このエラーの主な原因はmap同時書き込みである.

三、問題の原因


なぜmapの同時書き込みがこのエラーを引き起こすのですか?ネット上の関連記事にも多くの説明があります.
map変数はポインタタイプ変数であるため、同時書き込み時に複数のコモンシップが同時に1つのメモリを操作し、マルチスレッド操作のように同じリソースで競合関係が発生し、共有リソースが破壊されるためgolangは安全を考慮して致命的なエラー:fatal error:concurrent map writesを投げ出す.

四、解決方案


ネット上の各ルートの資料解決方案は比較的に多く、主な構想はロックをかけることによって各協程の同期操作メモリを保証することである.
githubでconcurrentMapパッケージが見つかりました.ケースコードの変更は次のとおりです.
package main

import (
	"fmt"
	cmap "github.com/orcaman/concurrent-map"
	"time"
)

var TestMap cmap.ConcurrentMap

func init() {
	TestMap = cmap.New()
}

func main() {
	for i := 0; i < 100; i++ {
		go Write("aaa", "111")
		go Read("aaa")
		go Write("bbb", "222")
		go Read("bbb")
	}
	time.Sleep(5 * time.Second)
}

func Read(key string) {
	if v, ok := TestMap.Get(key); ok {
		fmt.Printf("  %s  :%s", key, v)
	} else {
		fmt.Printf(" ")
	}
}

func Write(key string, value string) {
	TestMap.Set(key, value)
}

五、思考の総括


私はPHPで开いたプログラミングの世界なので、PHP言语は単一のスレッドしかなくて、しかもポインタの操作に関连しないで、変数のタイプも弱い変数で、PHPでプログラミングの思考でGolangに接触し始めたばかりの时はまた比较的に手に入れやすくて、しかし后で、言语の特性の区别はますます明らかに体現して、思考の変化はますます大きくて、私にとって1つの新しい世界を开きました.
本文のような誤ったケースも、自分がマルチスレッドプログラミングの思考基盤を持っていないため、このような問題に敏感ではないため、多くの時間をかけて理解しています.私と似たような学習ルートを持っている友达に助けてほしいです.