go接收者和鎖注意事項
阿新 • • 發佈:2021-08-07
go接收者和鎖注意事項
-
如果需要修改物件中的值,建議使用指標接收者。如果不需要修改物件中的值,建議使用值接收者。
-
程式碼
package main import ( "fmt" ) type Student struct { Name string Age int } func (s Student) UpdateName(name string) { s.Name = name } func (s *Student) UpdateAge(age int) { s.Age = age } func main() { s := Student{Name: "jom", Age: 20} fmt.Println(s) s.UpdateName("tim") fmt.Println(s) s.UpdateAge(22) fmt.Println(s) }
-
輸出
{jom 20} {jom 20} {jom 22}
-
從輸出可以分析出來,當為值接收者的時候,也就是
(s Student)
,是不會修改物件值的。當為指標接收者的時候,也就是(s *Student)
,是會修改物件值的。從程式碼上看,這是因為無論何時呼叫UpdateName
函式,s
都會是一份值拷貝,無論怎麼修改UpdateName
函式中s
的值,都不會對main
函式中的s
值有任何的影響。而UpdateAge
函式,s
是指標,也就是在呼叫的時候指標接收者s
的地址和main
函式中s
的地址是一致的,使用指標接收者進行修改,會對main
函式中的s
的值有影響。
-
-
鎖的拷貝是建立新的鎖
- 程式碼
package main import ( "fmt" "sync" "time" ) type Class struct { sync.Mutex Student map[string]int } func (c Class) inc(key string) { c.Lock() defer c.Unlock() c.Student[key]++ } func main() { c := Class{Student: map[string]int{"aa": 0, "bb": 0}} do := func(key string, frequency int) { for i := 0; i < frequency; i++ { c.inc(key) } } go do("aa", 1000) go do("aa", 1000) time.Sleep(10 * time.Second) fmt.Println(c.Student) }
- 輸出
fatal error: concurrent map writes
- 從結果看來是
map
的讀寫衝突了。從程式碼上看,這是因為我們呼叫c.inc
函式的時候,對main
函式中的c進行了拷貝,而c
中欄位Mutex
由於是值型別,在拷貝的時候也拷貝了一份,而c
中的map
由於是引用型別,依然是指向同一個map
。這樣就會造成兩或兩個以上的協程同時修改map
中的值時,對map
的互斥失效,從而導致的寫衝突。