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

28

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

2个回答

50

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(),则其他go-routine要获取RLock()将需要等待X释放锁(Unlock())

3) 假设我们在处理Maps,是否可能出现“并发读写Map”错误?

  • Map不是线程安全的。因此,“并发读写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协程已经获取了RLock()时,另一个Go协程能否获取Lock()进行写操作,还是必须等待RLock()释放? 2)当某个人已经获取了Map的Lock()时,其他Go协程仍然可以获取RLock()吗? 3)假设我们在处理Maps,是否可能出现“并发读/写Map”错误? - Sharath BJ

2

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

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

如果一个goroutine持有RWMutex进行读取,并且另一个goroutine可能调用Lock,则没有goroutine应该期望能够获取读取锁,直到初始读取锁被释放。特别是,这禁止了递归读取锁定。这是为了确保锁最终变得可用;阻塞的Lock调用会排除新的读取者获取锁。


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

Go语言提供了通道来实现并发控制,因此我认为最有效的方式不是使用sync.lock,而是使用通道。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接