并发安全
并发安全也叫线程安全,在并发中出现了数据的丢失,称为并发不安全
map和slice都是并发不安全的
1、切片并发不安全
1.1、测试场景: 10000个协程同时添加切片
func main() {
var s []int
var wg sync.WaitGroup
wg.Add(10000)
for i := 0; i < 10000; i++ { //10000个协程同时添加切片
go func(i int){
defer wg.Done()
s = append(s, i)
}(i)
}
wg.Wait()
fmt.Println(len(s))
}
slice并发不安全
len小于10000,说明有数据丢失
1.2、解决方法: 加锁
func main() {
var s []int
var lock sync.RWMutex
var wg sync.WaitGroup
wg.Add(10000)
for i := 0; i < 10000; i++ { //10000个协程同时添加切片
go func(i int){
defer wg.Done()
lock.Lock()
s = append(s, i)
lock.Unlock()
}(i)
}
wg.Wait()
fmt.Println(len(s))
}
1.3、总结: slice在并发执行中不会报错,但是数据会丢失
2、map并发不安全
2.1、测试场景: 2个协程同时读和写
func main() {
var wg sync.WaitGroup
m := make(map[int]int)
sum := 0
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func(i int) {
defer wg.Done()
m[i] = i
}(i)
}
wg.Wait()
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func(i int) {
//开一个协程读map
defer wg.Done()
sum += m[i]
}(i)
}
wg.Wait()
fmt.Println(sum)
}
以上程序会直接奔溃中止运行
2.2、加写锁后,运行不会奔溃,但读数据会丢失,代码如下:
func main() {
var wg sync.WaitGroup
var lock sync.RWMutex
m := make(map[int]int)
sum := 0
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func(i int) {
defer wg.Done()
lock.Lock()
m[i] = i
lock.Unlock()
}(i)
}
wg.Wait()
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func(i int) {
//开一个协程读map
defer wg.Done()
sum += m[i]
}(i)
}
wg.Wait()
fmt.Println(sum)
}
sum值并不等于所有元素之和,说明数据存在丢失
2.3、加读写锁后,运行结果正确,代码如下:
func main() {
var wg sync.WaitGroup
var lock sync.RWMutex
m := make(map[int]int)
sum := 0
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func(i int) {
defer wg.Done()
lock.Lock()
m[i] = i
lock.Unlock()
}(i)
}
wg.Wait()
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func(i int) {
//开一个协程读map
defer wg.Done()
lock.Lock()
sum += m[i]
lock.Unlock()
}(i)
}
wg.Wait()
fmt.Println(sum)
}