Skip to content
鼓励作者:欢迎打赏犒劳

RWMutex读写锁

Demo示例

合理使用 RWMutex:

  • 写操作(initServices)使用 Lock()/Unlock()。
  • 读操作(getServices)使用 RLock()/RUnlock()。
  • 允许多个 goroutine 并发读,提高性能。
go
package main

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

// 模拟服务依赖结构
type ServiceDeps struct {
	AppName string
	Version string
}

var (
	depsMu sync.RWMutex
	deps   *ServiceDeps // 全局共享数据,只写一次,多次读
)

// 安全读取 deps
func getServices() *ServiceDeps {
	depsMu.RLock()
	defer depsMu.RUnlock()
	return deps
}

// 初始化 deps(模拟“单线程写”)
func initServices() {
	depsMu.Lock()
	defer depsMu.Unlock()

	// Mock 数据
	deps = &ServiceDeps{
		AppName: "PriceMonitor",
		Version: "v1.0.0",
	}
	fmt.Println("✅ Services initialized!")
}

func main() {
	// 模拟应用启动:单线程初始化
	initServices()

	// 模拟多个并发请求(goroutine)同时读取
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			time.Sleep(time.Millisecond * 10) // 模拟处理延迟

			s := getServices()
			if s != nil {
				fmt.Printf("Worker %d: App=%s, Version=%s\n", id, s.AppName, s.Version)
			} else {
				fmt.Printf("Worker % d: ❌ services not ready!\n", id)
			}
		}(i)
	}

	wg.Wait()
	fmt.Println("🎉 All workers done.")
}

结果

text
✅ Services initialized!
Worker 0: App=PriceMonitor, Version=v1.0.0
Worker 3: App=PriceMonitor, Version=v1.0.0
Worker 1: App=PriceMonitor, Version=v1.0.0
Worker 4: App=PriceMonitor, Version=v1.0.0
Worker 2: App=PriceMonitor, Version=v1.0.0
🎉 All workers done.

疑问

如果不加读锁会怎么样?

我之前以为,如果是读的话则可以多个请求读?写的话则会加锁,只有一个请求操作。那么感觉读锁没啥用,是否可以不显式的加读锁呢?好像貌似也没啥问题。

但其实上,加读锁是为了防止有写的情况。

  • 多个 goroutine 可以同时读(调用 RLock())✅
  • 写操作是独占的:只要有一个 goroutine 在写(Lock()),其他读和写都必须等待 ✅
  • 读和写不能同时进行:有读时不能写,有写时不能读 ✅

这正是“读写锁”(Read-Write Mutex)的核心语义。

注意,读锁和写锁是互斥的,所以多并发读的时候还是需要加上`RLock()/RUnlock()`的,避免并发修改

什么情况下可以不加锁?

  • 只有当满足以下所有条件时,才可以无锁读:
  • 变量在程序启动时一次性初始化(比如 init() 函数中)
  • 之后永远不再修改
  • 且初始化发生在 main 执行之前(或在 main 中单线程完成)

如有转载或 CV 的请标注本站原文地址