Golang 中的 RLock() 和 Lock() 有什么区别?

what is the difference between RLock() and Lock() in Golang?

Golang 中的 RLock() 和 Lock() 有什么区别,我们在使用互斥锁时如何有效地使用它们?

RWMutex 是 reader/writer 互斥锁。锁可以由任意数量的读者或单个作者持有。 RWMutex 的零值是未锁定的互斥量。

RWMutex 不能在第一次使用后复制。

如果一个 goroutine 持有一个 RWMutex 用于读取,而另一个 goroutine 可能调用 Lock,则在释放初始读锁之前,任何 goroutine 都不应期望能够获取读锁。特别是,这禁止递归读取锁定。这是为了确保锁最终可用;阻塞的 Lock 调用将新读者排除在获取锁之外。


Mu​​tex 是一种互斥锁。 Mutex 的零值是未锁定的互斥量。

golang 提供通道是并发控制的最佳实践,所以我认为使用 sync.lock 的有效方式没有使用它,而是使用通道。

Lock(): 一次只有一个go例程read/write通过获取锁

RLock(): 多个go routine可以通过获取锁一次读取(不能写入)

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {

    a := 0

    lock := sync.RWMutex{}

    for i := 1; i < 10; i++ {
        go func(i int) {
            lock.Lock()
            fmt.Printf("Lock: from go routine %d: a = %d\n",i, a)
            time.Sleep(time.Second)
            lock.Unlock()
        }(i)
    }

    b := 0

    for i := 11; i < 20; i++ {
        go func(i int) {
            lock.RLock()
            fmt.Printf("RLock: from go routine %d: b = %d\n",i, b)
            time.Sleep(time.Second)
            lock.RUnlock()
        }(i)
    }

    <-time.After(time.Second*10)
}

1) 当一个go-routine已经获取了一个RLock()时,另一个go-routine是否可以获取一个Lock()用于写入或者它必须等到RUnlock()发生?

  • 要获取用于写入的 Lock(),它必须等到 RUnlock()

2) 当有人已经为 map 获取了 Lock() 时会发生什么,其他 go-routine 是否仍然可以获取 RLock()

  • 如果某人 X 已经获取了 Lock(),那么其他获取 RLock() 的 go-routine 将不得不等到 X 释放锁(Unlock())

3)假设我们这里处理的是Maps,有没有可能出现"concurrent read/write of Map"错误?

  • 地图不是线程安全的。所以 "concurrent read/write of Map" 会导致错误。

有关更多说明,请参见以下示例:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    lock := sync.RWMutex{}

    b := map[string]int{}
    b["0"] = 0

    go func(i int) {
        lock.RLock()
        fmt.Printf("RLock: from go routine %d: b = %d\n",i, b["0"])
        time.Sleep(time.Second*3)
        fmt.Printf("RLock: from go routine %d: lock released\n",i)
        lock.RUnlock()
    }(1)

    go func(i int) {
        lock.Lock()
        b["2"] = i
        fmt.Printf("Lock: from go routine %d: b = %d\n",i, b["2"])
        time.Sleep(time.Second*3)
        fmt.Printf("Lock: from go routine %d: lock released\n",i)
        lock.Unlock()
    }(2)

    <-time.After(time.Second*8)

    fmt.Println("*************************************8")

    go func(i int) {
        lock.Lock()
        b["3"] = i
        fmt.Printf("Lock: from go routine %d: b = %d\n",i, b["3"])
        time.Sleep(time.Second*3)
        fmt.Printf("Lock: from go routine %d: lock released\n",i)
        lock.Unlock()
    }(3)

    go func(i int) {
        lock.RLock()
        fmt.Printf("RLock: from go routine %d: b = %d\n",i, b["3"])
        time.Sleep(time.Second*3)
        fmt.Printf("RLock: from go routine %d: lock released\n",i)
        lock.RUnlock()
    }(4)

    <-time.After(time.Second*8)
}