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()
}