1. 程式人生 > 其它 >golang中的map以及併發問題

golang中的map以及併發問題

技術標籤:golang

特點

  1. 無序的鍵值對集合,使用key來獲取,無法通過index來獲取,返回順序未知,因此每次列印的順序可能不一樣。
  2. 可迭代 for range。
  3. 使用hash表實現的,是引用型別。
  4. len()獲取長度。
  5. key可以是所有可比較的型別:boolean, numeric, string, pointer, channel, interface, array, struct。
  6. 非併發安全的。

宣告與初始化

var m1 map[string]int // 只是宣告,依然是 nil
m2 := make(map[string]int) // 宣告並建立,也就是說已經分配地址了
m3 := map
[string]int{"golang":90, "python":90} m4 := map[string]int{} 如果只宣告而不初始化map,那麼就會建立一個nil map。將不能用來存放鍵值對,比如上面的m1。 if m1 == nil { // 空的 map m1 = make(map[string]int) } 通過key訪問value,訪問一個不存在的key,會返回value型別的零值,而不會報錯。 因此,無法通過值來判斷key是否存在,需要通過ok-idiom的方式 value, ok := map[key] if ok == true
{ // key是存在的 } else { // key是不存在的 }

設定值

m2[k1] = 100

如果k1不存在就是新增

刪除某個key

delete(m2, k1)

k1 不存在也不會報錯,並不會真的刪除,只是做了一個標記而已。

遍歷map

for key, value := range m2 {
	// 順序不是固定的
}

實現按一定順序遍歷map

把key單獨抽取出來,放在陣列中,對陣列進行排序,然後遍歷陣列即可。

map的併發問題

func testmap3() {
	m := map[int]int{}

	for i := 0; i < 21; i++ {
		go func
() { m[1] = 1 }() } }

報錯:fatal error: concurrent map writes

兩種初始化方式的對比

var wg sync.WaitGroup
var Dog map[int]int

func main() {
	num := 10
	wg.Add(num)

	for i := 0; i < num; i++ {
		go func() {
			defer wg.Done()
			add()
		}()
	}
	wg.Wait()
}

func add() {
	if Dog == nil {
        
         /*
			方法1
			Dog = map[int]int{
				1:  setV(),
				2:  setV(),
				3:  setV(),
				4:  setV(),
				5:  setV(),
				6:  setV(),
				7:  setV(),
				8:  setV(),
				9:  setV(),
				10: setV(),
			}
		*/
        
         // 方法2
		Dog = make(map[int]int)
		Dog[1] = setV()
		Dog[2] = setV()
		Dog[3] = setV()
		Dog[4] = setV()
		Dog[5] = setV()
		Dog[6] = setV()
		Dog[7] = setV()
		Dog[8] = setV()
		Dog[9] = setV()
		Dog[10] = setV()
	} else {
		fmt.Println(Dog[10])
	}
}

func setV() int {
	return rand.Intn(10000)
}

方法1 實際上是先初始化一個臨時變數,最後將其賦值給 Dog,即使出現了併發,也只會是後者覆蓋前者。但是方法2 會出現問題:

  1. concurrent map read and map write
  2. Dog[10] 可能還沒有被賦值。
  3. Dog 被分配了多次記憶體

也就說map的併發寫是會報錯的,所以需要規避這個問題。

內建的併發安全的map資料結構
sync.Map{}
	func (m *Map) Delete(key interface{})
    func (m *Map) Load(key interface{}) (value interface{}, ok bool)
    func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)
    func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
    func (m *Map) Range(f func(key, value interface{}) bool)
    func (m *Map) Store(key, value interface{})

通過加鎖
sync.Mutex
sync.RWMutex
sync.Once

map的GC回收機制

map是隻增不減的一種陣列結構,那些被 delete 的 key 會被打上標記,但是不會被GC回收。對於大的map物件。如果不再使用了最好使用賦值 nil 的方式使其整個的可以被GC。