sync包有以下幾個(gè)內(nèi)容:
(1)sync.Pool 臨時(shí)對(duì)象池
(2)sync.Mutex 互斥鎖
(3)sync.RWMutex 讀寫互斥鎖
(4)sync.WaitGroup 組等待
(5)sync.Cond 條件等待
(6)sync.Once 單次執(zhí)行
一、臨時(shí)對(duì)象池 --- 2019年11月8日20:05更新
1、Pool是用于存儲(chǔ)那些被分配了但是沒有被使用,而未來可能會(huì)使用的值,以減小垃圾回收的壓力。
2、Pool是協(xié)程安全的,應(yīng)該用于管理協(xié)程共享的變量,不推薦用于非協(xié)程間的對(duì)象管理
3、調(diào)動(dòng) New 函數(shù),將使用函數(shù)創(chuàng)建一個(gè)新對(duì)象返回
4、從Pool中取出對(duì)象時(shí),如果Pool中沒有對(duì)象,將執(zhí)行New(),如果沒有對(duì)New進(jìn)行賦值,則返回Nil。
5、先放入的后出來,和棧類似
Pool一個(gè)比較好的例子是fmt包,fmt包總是需要使用一些[]byte之類的對(duì)象,golang建立了一個(gè)臨時(shí)對(duì)象池,存放著這些對(duì)象,如果需要使用一個(gè)[]byte,就去Pool里面拿,如果拿不到就分配一份。這比起不停生成新的[]byte,用完了再等待gc回收來要高效得多
type Pool struct {
// 創(chuàng)建臨時(shí)對(duì)象的函數(shù)
New func() interface{}
}
// 向臨時(shí)對(duì)象池中存入對(duì)象
func (p *Pool) Put() x interface{}
// 向臨時(shí)對(duì)象池中取出對(duì)象
func (p *Pool) Get() interface{}
案例:
package main
import (
"fmt"
"sync"
)
func main() {
var pool sync.Pool
var val interface{}
pool.Put("1")
pool.Put(12)
pool.Put(true)
for {
val = pool.Get()
if val == nil {
break
}
fmt.Println(val)
}
}
二、互斥鎖
互斥鎖用來保證在任一時(shí)刻,只能有一個(gè)協(xié)程訪問某對(duì)象。Mutex的初始值為解鎖狀態(tài),Mutex通常作為其它結(jié)構(gòu)體的匿名字段使用,使該結(jié)構(gòu)體具有Lock和Unlock方法。
Mutex可以安全的再多個(gè)協(xié)程中并行使用。
注意:如果對(duì)未加鎖的進(jìn)行解鎖,則會(huì)引發(fā)panic。
加鎖,保證數(shù)據(jù)能夠正確:
package main
import (
"fmt"
"sync"
)
var num int
var mu sync.Mutex
var wg sync.WaitGroup
func main() {
wg.Add(10000)
for i := 0; i < 10000; i++ {
go Add()
}
wg.Wait()
fmt.Println(num)
}
func Add() {
mu.Lock() // 加鎖
defer func() {
mu.Unlock() // 解鎖
wg.Done()
}()
num++
}
并發(fā)沒有加鎖,如下代碼,當(dāng)你執(zhí)行下面代碼時(shí),會(huì)發(fā)現(xiàn)結(jié)果不等于10000,其原因是因?yàn)槌霈F(xiàn)這樣的情況,就是其中一些協(xié)程剛好讀取了num的值,此時(shí)該協(xié)程剛好時(shí)間片結(jié)束,被掛起,沒有完成加1。然后其他協(xié)程進(jìn)行加1,然后當(dāng)調(diào)度回到原來那個(gè)協(xié)程,num = 原來的那個(gè)num(而不是最新的num) + 1,導(dǎo)致num數(shù)據(jù)出錯(cuò):
package main
import (
"fmt"
"sync"
)
var num int
var wg sync.WaitGroup
func main() {
wg.Add(10000)
for i := 0; i < 10000; i++ {
go Add()
}
wg.Wait()
fmt.Println(num)
}
func Add() {
defer wg.Done()
num++
}
三、讀寫互斥鎖
RWMutex比Mutex多了一個(gè)“寫鎖定” 和 “讀鎖定”,可以讓多個(gè)協(xié)程同時(shí)讀取某對(duì)象。RWMutex的初始值為解鎖狀態(tài)。RWMutex通常作為其它結(jié)構(gòu)體的匿名字段使用。
RWMutex可以安全的在多個(gè)協(xié)程中并行使用。
// Lock 將 rw 設(shè)置為寫狀態(tài),禁止其他協(xié)程讀取或?qū)懭?func (rw *RWMutex) Lock()
// Unlock 解除 rw 的寫鎖定狀態(tài),如果rw未被鎖定,則該操作會(huì)引發(fā) panic。
func (rw *RWMutex) Unlock()
// RLock 將 rw 設(shè)置為鎖定狀態(tài),禁止其他協(xié)程寫入,但可以讀取。
func (rw *RWMutex) RLock()
// Runlock 解除 rw 設(shè)置為讀鎖定狀態(tài),如果rw未被鎖定,則該操作會(huì)引發(fā) panic。
func (rw *RWMutex) RUnLock()
// RLocker 返回一個(gè)互斥鎖,將 rw.RLock 和 rw.RUnlock 封裝成一個(gè) Locker 接口。
func (rw *RWMutex) RLocker() Locker
四、組等待
WaitGroup 用于等待一組協(xié)程的結(jié)束。主協(xié)程在創(chuàng)建每個(gè)子協(xié)程的時(shí)候先調(diào)用Add增加等待計(jì)數(shù),每個(gè)子協(xié)程在結(jié)束時(shí)調(diào)用 Done 減少協(xié)程計(jì)數(shù)。之后,主協(xié)程通過 Wait 方法開始等待,直到計(jì)數(shù)器歸零才繼續(xù)執(zhí)行。
// 計(jì)數(shù)器增加 delta,delte可以時(shí)負(fù)數(shù)
func (wg *WaitGroup) Add(delta int)
// 計(jì)數(shù)器減少1,等價(jià)于Add(-1)
func (wg *WaitGroup) Done()
// 等待直到計(jì)數(shù)器歸零。如果計(jì)數(shù)器小于0,則該操作會(huì)引發(fā) panic。
func (wg *WaitGroup) Wait()
五、條件等待
條件等待和互斥鎖有不同,互斥鎖是不同協(xié)程公用一個(gè)鎖,條件等待是不同協(xié)程各用一個(gè)鎖,但是wait()方法調(diào)用會(huì)等待(阻塞),直到有信號(hào)發(fā)過來,不同協(xié)程是共用信號(hào)
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
cond := sync.NewCond(new(sync.Mutex))
for i := 0; i < 3; i++ {
go func(i int) {
fmt.Println("協(xié)程", i, "啟動(dòng)。。。")
wg.Add(1)
defer wg.Done()
cond.L.Lock()
fmt.Println("協(xié)程", i, "加鎖。。。")
cond.Wait()
fmt.Println("協(xié)程", i, "解鎖。。。")
cond.L.Unlock()
}(i)
}
time.Sleep(2e9)
fmt.Println("主協(xié)程發(fā)送信號(hào)量。。。")
cond.Signal()
time.Sleep(2e9)
fmt.Println("主協(xié)程發(fā)送信號(hào)量。。。")
cond.Signal()
time.Sleep(2e9)
fmt.Println("主協(xié)程發(fā)送信號(hào)量。。。")
cond.Signal()
wg.Wait()
}
六、單次執(zhí)行
Once的作用是多次調(diào)用但只執(zhí)行一次,Once只有一個(gè)方法,Once.Do(),向Do傳入一個(gè)函數(shù),這個(gè)函數(shù)在第一次執(zhí)行Once.Do()的時(shí)候會(huì)被調(diào)用,以后再執(zhí)行Once.Do()將沒有任何動(dòng)作,即使傳入了其他的函數(shù),也不會(huì)被執(zhí)行,如果要執(zhí)行其它函數(shù),需要重新創(chuàng)建一個(gè)Once對(duì)象。
Once可以安全的再多個(gè)協(xié)程中并行使用。是協(xié)程安全的
標(biāo)準(zhǔn)庫(kù)中原型:
// 多次調(diào)用僅執(zhí)行一次指定的函數(shù)f
func (o *Once) Do(f func())
// 示例:Once
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
var wg sync.WaitGroup
onceFunc := func() {
fmt.Println("hello")
}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
once.Do(onceFunc) // 多次調(diào)用只執(zhí)行一次
}()
}
wg.Wait()
}
參考:
(1)https://www.cnblogs.com/golove/p/5918082.html
(2)https://blog.csdn.net/wangshubo1989/article/details/77966432?locationNum=9&fps=1