并发安全

并发安全也叫线程安全,在并发中出现了数据的丢失,称为并发不安全
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)
}

2.4、总结: map在并发执行中并不安全,需要加读写锁,或者使用sync.Map 代替,sync.Map 是并发安全的,内部也是基于锁机制避免程序异常。