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

时间:2018-11-22 09:36:28

标签: go mutex

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

2 个答案:

答案 0 :(得分:2)

Lock():通过获取锁一次只能执行一次常规例程读/写。

RLock():通过获取锁,多次执行例程可以一次读取(不写入)。

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)当一个例程已经获取了一个RLock()时,另一个例程可以获取一个Lock()进行写操作还是必须等待RUnlock()发生?

  • 要获取Lock()进行写入,必须等到RUnlock()

2)当某人已经为地图获取Lock()时,其他go-routine仍然可以获取RLock()

  • 如果某人X已经获取了Lock(),则其他获取RLock()的例程将不得不等到X释放锁(Unlock())

3)假设我们在这里处理地图,是否有可能发生“并发读取/写入地图”错误?

  • 地图不是线程安全的。因此“同时读取/写入地图”可能会导致错误。

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

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)
}

答案 1 :(得分:1)

RWMutex是读取器/写入器互斥锁。锁可以由任意数量的读取器或单个写入器持有。 RWMutex的零值是未锁定的互斥锁。

第一次使用后不得复制RWMutex。

如果goroutine拥有RWMutex进行读取,而另一个goroutine可能会调用Lock,则在释放初始读取锁之前,任何goroutine都不应期望能够获取读取锁。特别是,这禁止了递归读取锁定。这是为了确保锁最终可用。锁定的锁定呼叫被阻止,使新读者无法获得锁定。


互斥锁是互斥锁。互斥锁的零值是未锁定的互斥锁。

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