【问题标题】:what is the difference between RLock() and Lock() in Golang?Golang 中的 RLock() 和 Lock() 有什么区别?
【发布时间】:2018-11-22 09:36:28
【问题描述】:

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

【问题讨论】:

    标签: go mutex


    【解决方案1】:

    Lock():通过获取锁一次只能读取/写入一个 goroutine。

    RLock():多个 goroutine 可以通过获取锁一次读取(而不是写入)。

    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,有没有可能出现“并发读/写 Maps”错误?

    • 映射不是线程安全的。所以“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)
    }
    

    【讨论】:

    • 感谢您的回答,有一些问题 1)当一个 go-routine 已经获得了 RLock() 时,另一个 go-routine 是否可以获取 Lock() 进行写入或必须等到 RUnlock () 发生? 2) 当有人已经为 map 获取 Lock() 时会发生什么,其他 go-routine 是否仍然可以获得 RLock() 3) 假设我们在这里处理 Maps,是否有可能出现“并发读/写 Map”错误?来吗?
    【解决方案2】:

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

    RWMutex 首次使用后不得复制。

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


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

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

    【讨论】:

      猜你喜欢
      • 2014-05-18
      • 2016-08-16
      • 1970-01-01
      • 2017-12-07
      • 2016-10-07
      • 1970-01-01
      • 1970-01-01
      • 2020-08-08
      • 2012-10-06
      相关资源
      最近更新 更多