Go語言基礎-sync包

來源:互聯網
上載者:User

Golang 推薦通過 channel 進行通訊和同步,但在實際開發中 sync 包用得也非常的多,在以太坊的源碼中也有很多這類應用的體現。
Go sync包提供了:sync.Mutex,sync.RMutex,sync.Once,sync.Cond,sync.Waitgroup,sync.atomic等,文本主要對sync.Mutex,sync.RMutex和sync.Waitgroup的使用進行了說明,後續會推出其它方法的使用說明。

sync包含一個 Locker interface:

type Locker interface {        Lock()        Unlock()}

該介面只有兩個方法,Lock() 和 Unlock()。整個sync包都是圍繞該介面實現。

互斥鎖Mutex

互斥鎖Mutex是Locker的一種具體實現,有兩個方法:

func (m *Mutex) Lock()func (m *Mutex) Unlock()

一個互斥鎖只能同時被一個 goroutine 鎖定,其它 goroutine 將阻塞直到互斥鎖被解鎖,之後再重新爭搶對互斥鎖的鎖定。

對一個未鎖定的互斥鎖解鎖將會產生執行階段錯誤。

package mainimport (    "fmt"    "sync"    "time")func main() {    ch := make(chan struct{}, 2)    var l sync.Mutex    go func() {        l.Lock()        defer l.Unlock()        fmt.Println("goroutine1: 鎖定大概 2s")        time.Sleep(time.Second * 2)        fmt.Println("goroutine1: 解鎖了,可以開搶了!")        ch <- struct{}{}    }()    go func() {        fmt.Println("groutine2: 等待解鎖")        l.Lock()        defer l.Unlock()        fmt.Println("goroutine2: 我鎖定了")        ch <- struct{}{}    }()    // 等待 goroutine 執行結束    for i := 0; i < 2; i++ {        <-ch    }}

讀寫鎖 RWMutex

讀寫鎖是針對讀寫操作的互斥鎖,讀寫鎖與互斥鎖最大的不同就是可以分別對 讀、寫 進行鎖定。一般用在大量讀操作、少量寫操作的情況:

func (rw *RWMutex) Lock()func (rw *RWMutex) Unlock()func (rw *RWMutex) RLock()func (rw *RWMutex) RUnlock()

由於這裡需要區分讀寫鎖定,讀寫鎖這樣定義:

  • 讀鎖定(RLock),對讀操作進行鎖定

  • 讀解鎖(RUnlock),對讀鎖定進行解鎖

  • 寫鎖定(Lock),對寫操作進行鎖定

  • 寫解鎖(Unlock),對寫鎖定進行解鎖

不要混用鎖定和解鎖,如:Lock 和 RUnlock、RLock 和 Unlock。因為對未讀鎖定的讀寫鎖進行讀解鎖或對未寫鎖定的讀寫鎖進行寫解鎖將會引起執行階段錯誤。

如何理解讀寫鎖呢?

  • 同時只能有一個 goroutine 能夠獲得寫鎖定。

  • 同時可以有任意多個 gorouinte 獲得讀鎖定。

  • 同時只能存在寫鎖定或讀鎖定(讀和寫互斥)。

也就是說,當有一個 goroutine 獲得寫鎖定,其它無論是讀鎖定還是寫鎖定都將阻塞直到寫解鎖;當有一個 goroutine 獲得讀鎖定,其它讀鎖定仍然可以繼續;當有一個或任意多個讀鎖定,寫鎖定將等待所有讀鎖定解鎖之後才能夠進行寫鎖定。所以說這裡的讀鎖定(RLock)目的其實是告訴寫鎖定:有很多人正在讀取資料,需等它們讀(讀解鎖)完再來寫(寫鎖定)。

package mainimport (    "fmt"    "math/rand"    "sync")var count intvar rw sync.RWMutexfunc main() {    ch := make(chan struct{}, 10)    for i := 0; i < 5; i++ {        go read(i, ch)    }    for i := 0; i < 5; i++ {        go write(i, ch)    }    for i := 0; i < 10; i++ {        <-ch    }}func read(n int, ch chan struct{}) {    rw.RLock()    fmt.Printf("goroutine %d 進入讀操作...\n", n)    v := count    fmt.Printf("goroutine %d 讀取結束,值為:%d\n", n, v)    rw.RUnlock()    ch <- struct{}{}}func write(n int, ch chan struct{}) {    rw.Lock()    fmt.Printf("goroutine %d 進入寫操作...\n", n)    v := rand.Intn(1000)    count = v    fmt.Printf("goroutine %d 寫入結束,新值為:%d\n", n, v)    rw.Unlock()    ch <- struct{}{}}

WaitGroup

WaitGroup 用於等待一組 goroutine 結束,用法比較簡單。它有三個方法:

func (wg *WaitGroup) Add(delta int)func (wg *WaitGroup) Done()func (wg *WaitGroup) Wait()

Add 用來添加 goroutine 的個數。Done 執行一次數量減 1。Wait 用來等待結束:

package mainimport (    "fmt"    "sync")func main() {    var wg sync.WaitGroup    for i, s := range seconds {        // 計數加 1        wg.Add(1)        go func(i, s int) {            // 計數減 1            defer wg.Done()            fmt.Printf("goroutine%d 結束\n", i)        }(i, s)    }        // 等待執行結束    wg.Wait()    fmt.Println("所有 goroutine 執行結束")}

注意,wg.Add() 方法一定要在 goroutine 開始前執行。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.