Go语言-如何保证slice的协程安全

发现问题

今天在看代码的时候,遇见了多个协程写同一个slice的情况,发现未对slice做任何保护,亦未使用其他手段保证并发安全,这样肯定会出错的。

思考

slice不是协程安全的,所以在多个协程中读写slice是不安全的,在高并发的情况下会产生不可控制的错误。

总结

这里记录一下错误的使用方式与正确的使用方式:

错误的使用方式:

var a []int

for i := 0; i < 10000; i++ {
    go func() {
        a = append(a, 1) // 多协程并发读写slice
    }()
}

fmt.Println(len(a))

输出结果可能不等于期望的值

第一种方式:

对slice加锁,进行保护

   num := 10000

    var a []int
    var l sync.Mutex

    var wg sync.WaitGroup
    wg.Add(num)

    for i := 0; i < num; i++ {
        go func() {
            l.Lock() // 加锁
            a = append(a, 1)
            l.Unlock() // 解锁
            wg.Done()
        }()
    }

    wg.Wait()

    fmt.Println(len(a))

缺点:锁会影响性能

第二种方式:

使用channel的传递数据

    num := 10000

    var wg sync.WaitGroup
    wg.Add(num)

    c := make(chan int)
    for i := 0; i < num; i++ {
        go func() {
            c <- 1 // channl是协程安全的
            wg.Done()
        }()
    }

    // 等待关闭channel
    go func() {
        wg.Wait()
        close(c)
    }()

    // 读取数据
    var a []int
    for i := range c {
        a = append(a, i)
    }

    fmt.Println(len(a))

第三种方式:

改用数组

    num := 10000

    a := make([]int, num, num)

    var wg sync.WaitGroup
    wg.Add(num)

    for i := 0; i < num; i++ {
        k := i // 必须使用局部变量
        go func(idx int) {
            a[idx] = 1
            wg.Done()
        }(k)
    }

    wg.Wait()

    count := 0
    for i := range a {
        if a[i] != 0 {
            count++
        }
    }
    fmt.Println(count)

优点:无锁,不影响性能

猜你喜欢

转载自www.cnblogs.com/zcqkk/p/11772173.html