1. 程式人生 > 其它 >go接收者和鎖注意事項

go接收者和鎖注意事項

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的互斥失效,從而導致的寫衝突。