Go语言并发编程中 RWMutex 是如何实现的

发布于 1周前 作者 yuanlaile 最后一次编辑是 5天前 来自 问答

Go语言并发编程中 RWMutex 是如何实现的

通过记录 readerCount 读锁的数量来进行控制,当有一个写锁的时候,会将读 锁数量设置为负数 1<<30。目的是让新进入的读锁等待之前的写锁释放通知读 锁。同样的当有写锁进行抢占时,也会等待之前的读锁都释放完毕,才会开始

进行后续的操作。 而等写锁释放完之后,会将值重新加上 1<<30, 并通知刚才 新进入的读锁(rw.readerSem),两者互相限制。

1 回复

在Go语言中,sync.RWMutex 是一种读写互斥锁,它允许对共享资源的并发访问,同时优化了读操作的性能。RWMutex 通过允许多个goroutine同时进行读操作,但写操作会阻塞其他读操作和写操作,来减少锁竞争,从而提高并发性能。

实现原理

RWMutex 的实现主要依赖于内部的两个互斥锁(通常是一个互斥锁用于写操作和一个信号量或计数器用于读操作)以及一个计数器来跟踪当前有多少个goroutine正在读取资源。

  1. 写锁:当请求写锁时,如果已经有读锁或写锁被持有,请求将阻塞,直到所有读锁和写锁都被释放。
  2. 读锁:当请求读锁时,如果有写锁被持有,请求将阻塞。但如果有其他读锁正在被持有,新的读锁请求将被允许,同时读锁计数器会增加。

示例代码

以下是一个使用 sync.RWMutex 的简单示例,演示了如何在Go中利用它来保护共享资源的读写操作。

package main

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

var (
    data     = "initial data"
    rwMutex  sync.RWMutex
)

func readData(id int) {
    rwMutex.RLock() // 加读锁
    defer rwMutex.RUnlock() // 释放读锁
    fmt.Printf("Goroutine %d: %s\n", id, data)
}

func writeData(id int, newData string) {
    rwMutex.Lock() // 加写锁
    defer rwMutex.Unlock() // 释放写锁
    fmt.Printf("Goroutine %d: Writing %s\n", id, newData)
    data = newData
}

func main() {
    // 启动多个goroutine读取数据
    for i := 1; i <= 3; i++ {
        go readData(i)
    }

    // 等待一会儿,确保读操作先开始
    time.Sleep(100 * time.Millisecond)

    // 启动写操作
    go writeData(4, "new data")

    // 等待足够的时间以查看输出
    time.Sleep(500 * time.Millisecond)
}

// 输出示例(顺序可能不同)
// Goroutine 1: initial data
// Goroutine 2: initial data
// Goroutine 4: Writing new data
// Goroutine 3: initial data
// 注意:实际输出中,写操作可能会阻塞读操作(如果写操作发生在读操作之后请求锁),但在这个例子中,由于写操作在多数读操作之后启动,所以读操作能够先完成。

注意

  • 在实际应用中,写操作的执行时间应该尽可能短,以避免长时间阻塞读操作。
  • 读写锁非常适合读多写少的场景,但如果写操作非常频繁,可能会导致读操作频繁阻塞,反而降低性能。
  • 使用 RWMutex 时,要确保锁的粒度适当,避免过度锁定或锁定不足。
回到顶部