package main
import (
"fmt"
"sync"
)
//高并发写入
//1. 加锁
func main1() {
slc := []int{}
n := 10000
var wg sync.WaitGroup
var lock sync.Mutex
wg.Add(n)
for i := 0; i < n; i++ {
go func(a int) {
lock.Lock()
slc = append(slc, a)
lock.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("done len:", len(slc))
}
//sync.Map
func main() {
var scene sync.Map
//将键值对保存到sync.Map
scene.Store("greece", 97)
scene.Store("london", 100)
scene.Store("egypt", 200)
// 从sync.Map中根据键取值
fmt.Println(scene.Load("london"))
// 根据键删除对应的键值对
scene.Delete("london")
// 遍历所有sync.Map中的键值对
scene.Range(func(k, v interface{}) bool {
fmt.Println("iterate:", k, v)
return true
})
}
在golang中map不是并发安全的,所有才有了sync.Map的实现,尽管sync.Map的引入确实从性能上面解决了map的并发安全问题,不过sync.Map却没有实现len()函数,这导致了在使用sync.Map的时候,一旦需要计算长度,就比较麻烦,
func main1() {
slc := map[int]int{}
var wg sync.WaitGroup
var lock sync.Mutex
i := 0
for i <1000 {
wg.Add(1)
go func(a int) {
defer wg.Done()
lock.Lock()
slc[1] = i
lock.Unlock()
}()
i++
}
wg.Wait()
fmt.Println(slc)
}
虽然这样map可以在工程里面使用,但是效果不好,毕竟加锁的话,就是对整个map进行加锁和解锁,导致整个map在加锁的过程中都被阻塞住,这种操作会严重影响性能。
func main02() {
test := sync.Map{}
var wg sync.WaitGroup
i := 0
for i < 1000 {
wg.Add(1)
go func() {
defer wg.Done()
test.Store(1,i)
}()
i++
}
wg.Wait()
fmt.Println(test.Load(1))
}
计算长度
func main() {
test := sync.Map{}
var wg sync.WaitGroup
i := 0
for i < 1000 {
wg.Add(1)
go func(i int) {
defer wg.Done()
test.LoadOrStore(i, 1)
}(i)
i++
}
wg.Wait()
len := 0
test.Range(func(k, v interface{}) bool {
len++
return true
})
fmt.Println("len of test:",len)
}
map加锁和sunc.map的区别:
在高并发场景下,使用互斥锁对map进行加锁会存在以下两个问题:
- 高并发下锁的竞争激烈。当多个goroutine同时访问同一个
map时,它们之间会产生大量的锁竞争,而这种锁竞争会导致大量的线程切换和CPU时间的浪费,同时也会影响程序的性能表现。 - 锁粒度过大。由于
map是一种非常常用的数据结构,可能会被众多的goroutine共享访问。如果直接用互斥锁来保护整个map,那么在高并发情况下,每次操作都需要等待获取锁,即使只有少量数据需要修改,也需要等待其他goroutine释放锁后才能进行。这样就会导致锁的粒度过大,降低了并发性能。
而sync.Map则是针对这种情况而设计的。它内部采用了一些优化技术来避免锁的过多使用和降低锁的粒度。具体来说:
sync.Map内部的键值对是通过多个bucket而非单一的哈希表实现的,不同的bucket之间没有锁竞争。这样就可以将锁的粒度缩小到每个bucket级别。sync.Map内部的锁被设计成了分段锁,不同的goroutine可以同时访问不同的bucket,降低了锁竞争的概率。在高并发环境下更加高效,可以提供更好的性能表现。sync.Map还提供了一些方法来支持安全的并发操作,例如Load()、Store()和Delete()等方法都是原子的,多个goroutine之间调用它们是没有竞争关系的,因此在高并发场景下使用更加安全可靠。
虽然使用互斥锁对map进行加锁也可以实现并发安全,但在高并发场景下锁的竞争会非常激烈,性能可能会有明显下降。而sync.Map则是针对这种情况而设计的,不仅提供了更好的并发安全保障,同时也优化了在高并发情况下的性能表现。
map遍历是无序的嘛?
不是的,Go语言中的map遍历是无序的。这是因为map的实现方式决定了其元素在内存中的存储顺序不固定,而且由于哈希函数的作用,不同的key可能会被分配到不同的位置上去。因此,在使用map遍历时不能依赖遍历的顺序,如果需要按照key或value的排序进行遍历,需要通过其他的数据结构来辅助实现。