go標(biāo)準(zhǔn)庫(kù)中sync包

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 更多精彩文章:https://deepzz.com Desc:Go sync 包的使用方法,sync.Mutex,...
    deepzz閱讀 2,106評(píng)論 3 9
  • sync包使用官方文檔:http://devdocs.io/go/sync/index#Map Go中sync包包...
    DDY26閱讀 1,617評(píng)論 0 1
  • 一個(gè)sync.Pool對(duì)象就是一組臨時(shí)對(duì)象的集合。Pool是協(xié)程安全的。Pool用于存儲(chǔ)那些被分配了但是沒有被使用...
    bigtom閱讀 21,955評(píng)論 17 9
  • 原文:http://developer.android.com/training/sync-adapters/cr...
    tiger桂閱讀 555評(píng)論 0 0
  • 其實(shí)心情蠻忐忑的,不安微微后悔,談不上后悔吧,總是有一絲的難受。 土豆說要來找我的時(shí)候,我有點(diǎn)震驚,她這么懶居然想...
    宋小朝閱讀 291評(píng)論 0 0

友情鏈接更多精彩內(nèi)容