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)

優點:無鎖,不影響性能


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM