Leong's blog Leong's blog
首页
  • 编程
  • 资源
  • Golang
  • 微服务
  • vue
  • 操作系统
  • 数据结构与算法
  • Linux
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Leong Y

跑起来吧
首页
  • 编程
  • 资源
  • Golang
  • 微服务
  • vue
  • 操作系统
  • 数据结构与算法
  • Linux
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • go基础

    • Go的深浅拷贝
    • 并发安全map
      • 1、Map是否安全?
      • 2、解决方案
        • 2.1、使用sync.Mutex
        • 2.2、使用sync.RWMutex
        • 2.3、使用sync.Map(⭐)
        • 2.3.1、声明
        • 2.3.2、存储键值对
        • 2.3.3、加载键值对
        • 2.3.4、删除键值对
        • 2.3.5、加载或存储键值对
        • 2.3.6、迭代键值对
      • 总结
  • go常用库

  • web开发

  • Golang
  • go基础
leong
2024-06-03
目录

并发安全map

# 1、Map是否安全?

在 Golang 中,原生的 map 是非并发安全的,也就是说如果多个 goroutine 同时对 map 进行读写操作,会导致竞态条件和数据不一致的问题。为了确保 map 的并发安全,可以使用多种方法,下面介绍几种常见的解决方案。

# 2、解决方案

# 2.1、使用sync.Mutex

通过使用互斥锁 sync.Mutex 来保护 map 的读写操作,当多个goroutine同时访问一个map时只有获得mutex锁的能执行,其他goroutine得阻塞直到锁释放才能继续,这样可以确保同一时间只有一个 goroutine 访问 map。

package main

import (
	"fmt"
	"sync"
)

type SafeMap struct {
	mu sync.Mutex
	m  map[string]int
}

func (sm *SafeMap) Set(key string, value int) {
	sm.mu.Lock()
	defer sm.mu.Unlock()
	sm.m[key] = value
}

func (sm *SafeMap) Get(key string) (int, bool) {
	sm.mu.Lock()
	defer sm.mu.Unlock()
	val, ok := sm.m[key]
	return val, ok
}

func main() {
	safeMap := SafeMap{
		m: make(map[string]int),
	}

	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			safeMap.Set(fmt.Sprintf("key%d", i), i)
		}(i)
	}

	wg.Wait()

	for i := 0; i < 10; i++ {
		key := fmt.Sprintf("key%d", i)
		value, ok := safeMap.Get(key)
		if ok {
			fmt.Printf("key: %s, value: %d\n", key, value)
		}
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 2.2、使用sync.RWMutex

sync.RWMutex 提供了读写锁功能,允许多个读操作同时进行,但写操作是互斥的。对于读多写少的场景,可以提高性能。

package main

import (
	"fmt"
	"sync"
)

type SafeMap struct {
	rw sync.RWMutex
	m  map[string]int
}

func (sm *SafeMap) Set(key string, value int) {
	sm.rw.Lock()
	defer sm.rw.Unlock()
	sm.m[key] = value
}

func (sm *SafeMap) Get(key string) (int, bool) {
	sm.rw.RLock()
	defer sm.rw.RUnlock()
	val, ok := sm.m[key]
	return val, ok
}

func main() {
	safeMap := SafeMap{
		m: make(map[string]int),
	}

	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			safeMap.Set(fmt.Sprintf("key%d", i), i)
		}(i)
	}

	wg.Wait()

	for i := 0; i < 10; i++ {
		key := fmt.Sprintf("key%d", i)
		value, ok := safeMap.Get(key)
		if ok {
			fmt.Printf("key: %s, value: %d\n", key, value)
		}
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 2.3、使用sync.Map(⭐)

# 2.3.1、声明

sync.Map 不像普通的 map 那样使用 make 创建,而是直接声明一个 sync.Map 变量即可。

var sm sync.Map
1

# 2.3.2、存储键值对

使用 Store 方法存储键值对。

sm.Store("key1", "value1")
1

# 2.3.3、加载键值对

使用 Load 方法加载键值对,如果键存在,返回对应的值和 true,否则返回 nil 和 false。

value, ok := sm.Load("key1")
if ok {
    fmt.Println("key1:", value)
} else {
    fmt.Println("key1 not found")
}
1
2
3
4
5
6

# 2.3.4、删除键值对

使用 Delete 方法删除指定键值对。

sm.Delete("key1")
1

# 2.3.5、加载或存储键值对

使用 LoadOrStore 方法,如果键存在则返回已存在的值和 true,否则存储新值并返回新值和 false。

value, loaded := sm.LoadOrStore("key2", "value2")
if loaded {
    fmt.Println("key2 already exists with value:", value)
} else {
    fmt.Println("key2 is set with value:", value)
}
1
2
3
4
5
6

# 2.3.6、迭代键值对

使用 Range 方法迭代所有键值对,参数是一个函数,函数返回 true 继续迭代,返回 false 停止迭代。

sm.Range(func(key, value interface{}) bool {
    fmt.Println("key:", key, "value:", value)
    return true
})
1
2
3
4

# 总结

以上三种方法都可以用来实现并发安全的 map,其中:

  • 使用 sync.Mutex 和 sync.RWMutex 需要手动管理锁的生命周期,适用于对锁的控制要求较高的场景。
  • 使用 sync.Map 则更为简单易用,适合频繁读写的高并发场景。
特性 sync.Mutex sync.RWMutex sync.Map
并发读写安全 是 是 是
实现复杂度 中 高 低
性能 中 高 高
适用场景 通用 读多写少 读写频繁
上次更新: 2024/07/08, 18:19:51
Go的深浅拷贝
Socket编程

← Go的深浅拷贝 Socket编程→

最近更新
01
vue3快速上手
07-31
02
程序从加载到运行的过程
07-08
03
进程、线程、协程
07-08
更多文章>
Theme by Vdoing | Copyright © 2023-2024 Leong Y | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式