golang,go,博客,开源,编程

golang之sync.Map

Published on with 0 views and 0 comments

sync.Map 是 Go 标准库 sync 包中的一个并发安全的映射(map)类型。它是为了解决普通 map 在多 goroutine 并发访问时的竞态问题而设计的。sync.Map 提供了更高效的并发操作,避免了显式使用锁(如 sync.Mutex)来保护 map 的访问。

特点

  • 并发安全sync.Map 支持并发读写,保证多 goroutine 同时对 map 的读写操作不会发生竞态条件。
  • 性能优化:它通过内部的 atomic 操作来提高性能,尤其是对读操作比较频繁的场景,它的性能通常优于使用显式锁的 map
  • 不支持直接索引:与普通的 map 不同,sync.Map 不支持使用普通的下标方式来进行访问(即 syncMap[key] 不可用)。你需要使用专门的 API 来进行操作。

sync.Map 的主要方法

sync.Map 提供了以下几个方法:

  1. Store(key, value):存储一个键值对。
  2. Load(key):读取一个值,如果键存在,返回该值和 true,否则返回 nilfalse
  3. LoadOrStore(key, value):如果键不存在,则将 keyvalue 存储在 map 中,并返回该值。如果键已经存在,则返回该键的现有值和 true
  4. Delete(key):删除指定的键值对。
  5. Range(f func(key, value interface{}) bool):遍历所有键值对,调用传入的函数。如果函数返回 false,遍历停止。

示例代码

package main

import (
	"fmt"
	"sync"
)

func main() {
	// 创建一个 sync.Map 实例
	var m sync.Map

	// 使用 Store 存储数据
	m.Store("name", "John")
	m.Store("age", 30)

	// 使用 Load 读取数据
	name, ok := m.Load("name")
	if ok {
		fmt.Println("Name:", name)
	} else {
		fmt.Println("Name not found")
	}

	// 使用 LoadOrStore,存在则返回现有值,不存在则存储
	age, _ := m.LoadOrStore("age", 25)
	fmt.Println("Age:", age)

	// 删除一个键
	m.Delete("age")

	// 再次检查 age 键
	age, ok = m.Load("age")
	if ok {
		fmt.Println("Age:", age)
	} else {
		fmt.Println("Age not found")
	}

	// 使用 Range 遍历所有键值对
	m.Store("city", "New York")
	m.Range(func(key, value interface{}) bool {
		fmt.Printf("%s: %v\n", key, value)
		return true // 返回 true 继续遍历
	})
}

输出

Name: John
Age: 30
Age not found
city: New York
name: John

详细解析

  1. Store(key, value)
    • Store 方法用来向 sync.Map 存储一个键值对。例如,m.Store("name", "John") 将键 "name" 和对应的值 "John" 存入 sync.Map 中。
  2. Load(key)
    • Load 方法用来读取键对应的值。如果键存在,返回该值以及一个布尔值 true;如果键不存在,则返回 nilfalse。例如,name, ok := m.Load("name")
  3. LoadOrStore(key, value)
    • LoadOrStore 方法首先检查键是否存在。如果存在,返回现有的值;如果不存在,存储给定的键值对并返回新值。与 LoadStore 不同,LoadOrStore 可以在读取时对不存在的键执行存储操作。例如,age, _ := m.LoadOrStore("age", 25) 如果 "age" 不存在,则存储并返回值 25
  4. Delete(key)
    • Delete 方法用来删除指定的键。例如,m.Delete("age") 将删除键 "age" 及其对应的值。
  5. Range(f func(key, value interface{}) bool)
    • Range 方法遍历 sync.Map 中的所有键值对,并为每个键值对调用传入的函数 f。该函数接受两个参数:keyvalue,并返回一个布尔值。如果返回 false,则停止遍历;如果返回 true,则继续遍历。

使用场景

sync.Map 适用于以下场景:

  • 读多写少:当你有大量并发读操作,并且写操作相对较少时,sync.Map 的性能优势尤其明显。
  • 缓存sync.Map 常被用作并发安全的缓存容器,特别是在不频繁变动的键值对场景中。
  • 共享数据:多个 goroutine 需要共享数据,但不希望显式地使用锁(例如 sync.Mutex)来同步访问时,sync.Map 是一个理想的选择。

注意事项

  1. 性能考量
    • 对于 写操作较多 的场景,sync.Map 性能不如普通的 map 加锁操作。在写多读少的场景下,使用普通的 mapsync.Mutexsync.RWMutex 可能会更高效。
  2. 键值对的生命周期
    • sync.Map 对键值对的生命周期没有特殊要求,可以在并发情况下安全地进行添加、删除和查询操作,但不支持直接对 map 进行迭代(需要通过 Range 方法)。
  3. 类型安全
    • sync.Map 的操作是基于 interface{} 类型的,因此,你需要在取出数据时进行类型断言。虽然它提供了灵活性,但也会带来一定的类型安全风险。在使用时,要确保类型断言正确。

总结

  • sync.Map 是 Go 提供的并发安全的映射类型,适用于读多写少的场景。
  • 提供了并发安全的读写方法,如 LoadStoreLoadOrStoreDelete
  • 通过 Range 方法提供了遍历功能,适用于并发场景下的共享数据存储。

如果你的应用场景中有大量的并发访问共享数据,特别是读多写少的情况,sync.Map 是一个非常好的选择。


标题:golang之sync.Map
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/01/07/1736220488702.html
联系:scotttu@163.com