郑文峰的博客 郑文峰的博客
首页
  • Go语言高性能编程
分类
标签
归档
关于
  • 导航 (opens new window)
  • 代码片段 (opens new window)
  • 收藏
  • 友链
  • 外部页面

    • 开往 (opens new window)
GitHub (opens new window)

zhengwenfeng

穷则变,变则通,通则久
首页
  • Go语言高性能编程
分类
标签
归档
关于
  • 导航 (opens new window)
  • 代码片段 (opens new window)
  • 收藏
  • 友链
  • 外部页面

    • 开往 (opens new window)
GitHub (opens new window)
  • python

  • go语言

    • go简单使用grpc
    • gin中validator模块的源码分析
    • 优化gin表单的错误提示信息
    • go中如何处理error
    • tcp缓存引起的日志丢失
    • 使用etcd分布式锁导致的协程泄露与死锁问题
    • go语言高性能编程

      • Go协程池深度解析:原理、实现与最佳实践
      • Go语言Interface Boxing原理与性能优化指南
      • Go语言遍历性能深度解析:从原理到优化实践
      • Go语言零拷贝技术完全指南
      • Go语言不可变数据共享:无锁并发编程实践
        • 前言
        • 案例一:共享配置
        • 案例二:不可变路由表
        • 总结
      • Go语言内存预分配完全指南
      • Go语言原子操作完全指南
      • Go语言堆栈分配与逃逸分析深度解析
      • Go语言空结构体:零内存消耗的高效编程
      • Go语言结构体内存对齐完全指南
      • Go语言字符串拼接性能对比与优化指南
      • Go语言延迟初始化(Lazy Initialization)最佳实践
      • Go语言高效IO缓冲技术详解
  • linux

  • 其他

  • 编程
  • go语言
  • go语言高性能编程
zhengwenfeng
2025-06-14
目录

Go语言不可变数据共享:无锁并发编程实践

# 前言

对共享数据的并发访问往往需要用到锁,而这是一个常见的性能瓶颈。而不可变数据共享式一种不需要用锁来保护共享数据的方式,创建后的数据永远不改变,这样就不会有竞争问题了。

# 案例一:共享配置

  1. 创建配置结构体
// config.go
type Config struct {
    LogLevel string
    Timeout  time.Duration
    Features map[string]bool // 必须深拷贝,原始map的修改会影响已创建的配置
}
1
2
3
4
5
6
  1. 每次获取配置都是独立的
func NewConfig(logLevel string, timeout time.Duration, features map[string]bool) *Config {
    copiedFeatures := make(map[string]bool, len(features))
    for k, v := range features {
        copiedFeatures[k] = v
    }

    return &Config{
        LogLevel: logLevel,
        Timeout:  timeout,
        Features: copiedFeatures,
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
  1. 使用 atomic.Value 来存储并安全的更新当前配置
var currentConfig atomic.Pointer[Config] // Go 1.19+ 特性

// LoadInitialConfig 初始化配置(必须保证线程安全)
func LoadInitialConfig() {
    cfg := NewConfig("info", 5*time.Second, map[string]bool{"beta": true})
    currentConfig.Store(cfg) // 原子存储初始配置
}

// GetConfig 安全获取当前配置(零锁消耗)
func GetConfig() *Config {
    return currentConfig.Load() // 原子加载指针
}
1
2
3
4
5
6
7
8
9
10
11
12
  1. 在处理程序中使用
func handler(w http.ResponseWriter, r *http.Request) {
    cfg := GetConfig()
    if cfg.Features["beta"] {
        // Enable beta path
    }
    // Use cfg.Timeout, cfg.LogLevel, etc.
}
1
2
3
4
5
6
7

# 案例二:不可变路由表

  1. 创建路由结构体
type Route struct {
    Path    string
    Backend string
}

type RoutingTable struct {
    Routes []Route
}
1
2
3
4
5
6
7
8
  1. 每次获取都是全新版本
‍func NewRoutingTable(routes []Route) *RoutingTable {
    copied := make([]Route, len(routes))
    copy(copied, routes)
    return &RoutingTable{Routes: copied}
}
1
2
3
4
5
  1. 以原子的方式存储及修改
var currentRoutes atomic.Pointer[RoutingTable]

func LoadInitialRoutes() {
    table := NewRoutingTable([]Route{
        {Path: "/api", Backend: "http://api.internal"},
        {Path: "/admin", Backend: "http://admin.internal"},
    })
    currentRoutes.Store(table)
}

func GetRoutingTable() *RoutingTable {
    return currentRoutes.Load()
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. 并发路由请求
func routeRequest(path string) string {
    table := GetRoutingTable()
    for _, route := range table.Routes {
        if strings.HasPrefix(path, route.Path) {
            return route.Backend
        }
    }
    return ""
}
1
2
3
4
5
6
7
8
9

# 总结

应用场景

  • 读多写少:配置信息、路由表等低频变更数据
  • 高性能要求:需要避免锁竞争的热点路径

注意事项

  • 深拷贝成本:当数据结构复杂时(嵌套map/slice),需要考虑拷贝性能
  • 内存消耗:每次更新都会创建新对象,需权衡内存与性能
  • 原子性保证:更新操作必须完全替换配置对象,避免部分更新
#go语言#go语言高性能编程
上次更新: 2025/06/14, 16:24:58
Go语言零拷贝技术完全指南
Go语言内存预分配完全指南

← Go语言零拷贝技术完全指南 Go语言内存预分配完全指南→

最近更新
01
Go语言高效IO缓冲技术详解
06-14
02
Go语言延迟初始化(Lazy Initialization)最佳实践
06-14
03
Go语言字符串拼接性能对比与优化指南
06-14
更多文章>
Theme by Vdoing | Copyright © 2022-2025 zhengwenfeng | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式