golang 中 sync.Mutex 和 sync.RWMutex

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

介紹

golang 中的 sync 包實現了兩種鎖:

  • Mutex:互斥鎖
  • RWMutex:讀寫鎖,RWMutex 基於 Mutex 實現

Mutex(互斥鎖)

  • Mutex 為互斥鎖,Lock() 加鎖,Unlock() 解鎖
  • 在一個 goroutine 獲得 Mutex 後,其他 goroutine 只能等到這個 goroutine 釋放該 Mutex
  • 使用 Lock() 加鎖後,不能再繼續對其加鎖,直到利用 Unlock() 解鎖後才能再加鎖
  • 在 Lock() 之前使用 Unlock() 會導致 panic 異常
  • 已經鎖定的 Mutex 並不與特定的 goroutine 相關聯,這樣可以利用一個 goroutine 對其加鎖,再利用其他 goroutine 對其解鎖
  • 在同一個 goroutine 中的 Mutex 解鎖之前再次進行加鎖,會導致死結
  • 適用於讀寫不確定,並且只有一個讀或者寫的情境

樣本

加鎖和解鎖樣本

package mainimport (    "time"    "fmt"    "sync")func main() {    var mutex sync.Mutex    fmt.Println("Lock the lock")    mutex.Lock()    fmt.Println("The lock is locked")    channels := make([]chan int, 4)    for i := 0; i < 4; i++ {        channels[i] = make(chan int)        go func(i int, c chan int) {            fmt.Println("Not lock: ", i)            mutex.Lock()            fmt.Println("Locked: ", i)            time.Sleep(time.Second)            fmt.Println("Unlock the lock: ", i)            mutex.Unlock()            c <- i        }(i, channels[i])    }    time.Sleep(time.Second)    fmt.Println("Unlock the lock")    mutex.Unlock()    time.Sleep(time.Second)    for _, c := range channels {        <-c    }}

程式輸出:

Lock the lockThe lock is lockedNot lock:  1Not lock:  2Not lock:  0Not lock:  3Unlock the lockLocked:  1Unlock the lock:  1Locked:  2Unlock the lock:  2Locked:  3Unlock the lock:  3Locked:  0Unlock the lock:  0

在解鎖之前加鎖會導致死結

package mainimport (    "fmt"    "sync")func main(){    var mutex sync.Mutex    mutex.Lock()    fmt.Println("Locked")    mutex.Lock()}

程式輸出:

Lockedfatal error: all goroutines are asleep - deadlock!

RWMutex(讀寫鎖)

  • RWMutex 是單寫多讀鎖,該鎖可以加多個讀鎖或者一個寫鎖
  • 讀鎖佔用的情況下會阻止寫,不會阻止讀,多個 goroutine 可以同時擷取讀鎖
  • 寫鎖會阻止其他 goroutine(無論讀和寫)進來,整個鎖由該 goroutine 獨佔
  • 適用於讀多寫少的情境

Lock() 和 Unlock()

  • Lock() 加寫鎖,Unlock() 解寫鎖
  • 如果在加寫鎖之前已經有其他的讀鎖和寫鎖,則 Lock() 會阻塞直到該鎖可用,為確保該鎖可用,已經阻塞的 Lock() 調用會從獲得的鎖中排除新的讀取器,即寫鎖許可權高於讀鎖,有寫鎖時優先進行寫鎖定
  • 在 Lock() 之前使用 Unlock() 會導致 panic 異常

RLock() 和 RUnlock()

  • RLock() 加讀鎖,RUnlock() 解讀鎖
  • RLock() 加讀鎖時,如果存在寫鎖,則無法加讀鎖;當只有讀鎖或者沒有鎖時,可以加讀鎖,讀鎖可以載入多個
  • RUnlock() 解讀鎖,RUnlock() 撤銷單詞 RLock() 調用,對於其他同時存在的讀鎖則沒有效果
  • 在沒有讀鎖的情況下調用 RUnlock() 會導致 panic 錯誤
  • RUnlock() 的個數不得多餘 RLock(),否則會導致 panic 錯誤

樣本

Lock() 和 Unlock()

package mainimport (    "sync"    "fmt"    "time")func main() {    var mutex *sync.RWMutex    mutex = new(sync.RWMutex)    fmt.Println("Lock the lock")    mutex.Lock()    fmt.Println("The lock is locked")    channels := make([]chan int, 4)    for i := 0; i < 4; i++ {        channels[i] = make(chan int)        go func(i int, c chan int) {            fmt.Println("Not lock: ", i)            mutex.Lock()            fmt.Println("Locked: ", i)            fmt.Println("Unlock the lock: ", i)            mutex.Unlock()            c <- i        }(i, channels[i])    }    time.Sleep(time.Second)    fmt.Println("Unlock the lock")    mutex.Unlock()    time.Sleep(time.Second)    for _, c := range channels {        <-c    }}

程式輸出:

Lock the lockThe lock is lockedNot lock:  0Not lock:  1Not lock:  2Not lock:  3Unlock the lockLocked:  0Unlock the lock:  0Locked:  2Unlock the lock:  2Locked:  3Unlock the lock:  3Locked:  1Unlock the lock:  1

Lock() 和 RLock()

package mainimport (    "sync"    "fmt"    "time")func main() {    var mutex *sync.RWMutex    mutex = new(sync.RWMutex)    fmt.Println("Lock the lock")    mutex.Lock()    fmt.Println("The lock is locked")    channels := make([]chan int, 4)    for i := 0; i < 4; i++ {        channels[i] = make(chan int)        go func(i int, c chan int) {            fmt.Println("Not read lock: ", i)            mutex.RLock()            fmt.Println("Read Locked: ", i)            fmt.Println("Unlock the read lock: ", i)            time.Sleep(time.Second)            mutex.RUnlock()            c <- i        }(i, channels[i])    }    time.Sleep(time.Second)    fmt.Println("Unlock the lock")    mutex.Unlock()    time.Sleep(time.Second)    for _, c := range channels {        <-c    }}

程式輸出:

Lock the lockThe lock is lockedNot read lock:  2Not read lock:  3Not read lock:  1Not read lock:  0Unlock the lockRead Locked:  2Read Locked:  1Unlock the read lock:  2Unlock the read lock:  1Read Locked:  0Read Locked:  3Unlock the read lock:  0Unlock the read lock:  3

Unlock() 使用之前不存在 Lock()

package mainimport (    "sync")func main(){    var rwmutex *sync.RWMutex    rwmutex = new(sync.RWMutex)    rwmutex.Unlock()}

程式輸出:

panic: sync: Unlock of unlocked RWMutex

RWMutex 使用不當導致的死結

樣本1:

package mainimport (    "sync")func main(){    var rwmutex *sync.RWMutex    rwmutex = new(sync.RWMutex)    rwmutex.Lock()    rwmutex.Lock()}

程式輸出:

fatal error: all goroutines are asleep - deadlock!

樣本2:

package mainimport (    "sync")func main(){    var rwmutex *sync.RWMutex    rwmutex = new(sync.RWMutex)    rwmutex.Lock()    rwmutex.RLock()}

程式輸出:

fatal error: all goroutines are asleep - deadlock!

RUnlock() 之前不存在 RLock()

package mainimport (    "sync")func main(){    var rwmutex *sync.RWMutex    rwmutex = new(sync.RWMutex)    rwmutex.RUnlock()}

程式輸出:

panic: sync: RUnlock of unlocked RWMutex

RUnlock() 個數多於 RLock()

package mainimport (    "sync")func main(){    var rwmutex *sync.RWMutex    rwmutex = new(sync.RWMutex)    rwmutex.RLock()    rwmutex.RLock()    rwmutex.RUnlock()    rwmutex.RUnlock()    rwmutex.RUnlock()}

程式輸出:

panic: sync: RUnlock of unlocked RWMutex

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.