go

sync.Map和struct的区别

区别

sync.Map是Go标准库中sync包提供的一个并发安全的映射(map)类型,而常规的结构体struct是Go语言的基础数据结构,用于定义和创建自定义类型的实例。以下是它们之间一些关键的区别:

1.并发安全

  • sync.Map:设计为并发安全的,无需额外的锁来保护对其的访问,适用于多个goroutine同时读写的场景。
  • struct:本身并不提供并发安全保证。如果需要在多个goroutine中安全地访问常规结构体的字段,你需要使用互斥锁(如 sync.Mutex 或 sync.RWMutex)来手动管理访问控制。

2. 用途和操作

  • sync.Map:专为键值对存储设计,提供了一组特定的方法(如 Load, Store, Delete, Range 等)来操作键值对。
  • struct:用于定义具有一组特定字段的自定义类型。这些字段可以是任何类型,包括基本类型、切片、映射、通道等,以及其他结构体。结构体提供了灵活的方式来组织和管理相关数据。

3. 类型安全

  • sync.Map:不是类型安全的,因为它的键和值都是interface{}类型,这意味着你可以存储任何类型的键值对,但在使用时需要进行类型断言。
  • struct:是类型安全的。结构体的字段在定义时就确定了类型,编译器会在编译时检查类型正确性。

4. 性能考虑

  • sync.Map:虽然提供了并发安全,但其性能可能不如使用互斥锁手动管理的常规映射,特别是在读操作远多于写操作的场景中。
  • struct:使用互斥锁来保护的常规结构体(或其包含的映射)在性能上可能更优,尤其是在读多写少的场景下,因为读写锁(sync.RWMutex)可以允许多个读操作并行执行,而写操作则需要独占锁。

例子

下面以一个存放用户信息的例子来说明sync.Map和struct的区别:

sync.Map

import (
    "sync"
    "fmt"
)

var users = sync.Map{}

func main() {
  // 添加用户
  users.Store(1, map[string]interface{}{
    "id":   1,
    "name": "John",
    "age":  30,
  })
  // 获取用户
  value := users.Load(1)
  // 修改用户信息
  value.(map[string]interface{})["age"] = 31
  // 动态添加字段
  value.(map[string]interface{})["email"] = "john@gmail.com"
  // 添加用户
  users.Store(2, map[string]interface{}{
    "id":   2,
    "name": "Mary",
    "age": 26,
  })
}

从上面代码中可以看到:

  • 不需要预定义结构
  • 通过key直接获取value
  • 支持动态添加field
  • 写操作atomic保证安全

所以sync.Map更适用于动态场景。

struct

import (
    "sync"
    "fmt"
)

type User struct {
  Id   int
  Name string
  Age  int
}

func main() {
  var users = make(map[int]*User)

  users[1] = &User{
    Id:   1,
    Name: "John",
    Age:  30,
  }
  // 获取用户
  user := users[1]
  // 修改用户信息
  user.Age = 31
  // 添加用户
  users[2] = &User{
    Id:   2,
    Name: "Mary",
    Age: 26,
  }
}

从上面代码中可以看到:

  • 需要提前定义User结构体
  • 获取/修改用户需要通过索引获取结构体指针
  • 不支持动态添加字段
  • map写操作不安全
分类: go