Golang學習筆記-sync

Mutex

  • sync.Mutex爲互斥鎖,同一時間只能有一個goroutine獲得互斥鎖。
  • 使用Lock()加鎖,Unlock()解鎖,加鎖前不能解鎖,加鎖後不能繼續加鎖。
  • 已經鎖定的 Mutex 並不與特定的 goroutine 相關聯,可以利用一個 goroutine 對其加鎖,再利用其他 goroutine 對其解鎖。
  • 適用於同一時間只能有一個goroutine訪問資源的場景。

下面的代碼如果不使用Mutex,輸出的會是1 1 2 2 3 3而不是1 2 3 1 2 3。

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	mutex sync.Mutex
)

func print123(){
	mutex.Lock()
	defer mutex.Unlock()
	for i:=0;i<3;i++{
		fmt.Println(i+1)
		time.Sleep(time.Millisecond*100)
	}
}

func main(){
	go print123()
	go print123()
	time.Sleep(time.Second*5)
}

RWMutex

  • sync.RWMutex爲讀寫鎖,同一時間可以有多個goroutine獲得讀鎖或者一個goroutine獲得寫鎖。
  • 使用Lock()加寫鎖,Unlock()解寫鎖,RLock()加讀鎖,RUnlock()解讀鎖,讀鎖可以加多個,解讀鎖的次數不能多於加讀鎖的次數。
  • 適用於同一時間可以有多個goroutine對資源進行讀操作或一個goroutine對資源進行寫操作的場景。
  • 讀少寫多的情況下使用Mutex,其它情況下RWMutex性能更好。

Map

Go內建的map不是線程安全的,所以之前都會使用加鎖的方式控制對map的併發訪問,而在新版本中可以使用sync.Map代替map+RWMutex,sync.Map相比後者有更好的性能,下面是sync.Map的基本使用方法:

package main

import (
	"fmt"
	"sync"
)

var (
	players sync.Map
)



func main(){
	//設置key對應的value
	players.Store("xiao ming",100)
	//返回key對應的valuee,value不存在時返回nil,false
	if value,ok:=players.Load("xiao ming");ok{
		fmt.Println(value)
	}else{
		fmt.Println("key:xiao ming does not exist!")
	}
	//如果key對應的value存在,則返回value和true
	//如果key對應的value不存在,則將key對應的value設置爲參數中的value,並返回value和false
	if value,ok := players.LoadOrStore("xiao hong",120);ok{
		fmt.Println(value)
	}else{
		fmt.Println("key:xiao hong does not exist,store it!")
	}
	//遍歷map,函數返回false時停止遍歷
	players.Range(func(key,value interface{})bool{
		fmt.Println(key,value)
		return true
	})
	//刪除key對應的value
	players.Delete("xiao hong")
}

atomic

sync.atomic提供了對幾種簡單類型進行原子操作的函數(LoadXXX,StoreXXX),相比Mutex/RWMutex,其性能更好,臨界區也更小。

Once

sync.Once可以使函數只被調用一次:

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	closeOnce sync.Once
)

func closeSomething(){
	fmt.Println("close object")
}

func invokeClose(){
	closeOnce.Do(closeSomething)
}

func main() {
	go invokeClose()
	go invokeClose()
	time.Sleep(time.Second*5)
}

WaitGroup

sync.WaitGroup常用來等待其它goroutine結束,Add()增加計數器,Done()減少計數器,Wait()會一直阻塞當前goroutine直到計數器爲0。

常見用法爲調用Add()增加計數器,啓動goroutine,調用Wait()等待goroutine結束,goroutine結束時調用Done()減少計數器。

需要注意的是不能讓計數器爲負數,否則會報錯。另外sync.WaitGroup是值類型,所以在傳值的時候需要用指針。

package main

import (
	"sync"
	"time"
)

var (
	waitGroup = &sync.WaitGroup{}
)

func doSomething(){
	defer waitGroup.Done()
	time.Sleep(time.Second*5)
}



func main() {
	waitGroup.Add(1)
	go doSomething()
	waitGroup.Wait()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章