1. 程式人生 > 其它 >Golang語言切片slice的執行緒協程安全問題

Golang語言切片slice的執行緒協程安全問題

package mainimport (       "fmt"       "sync")func main() {

       sourceArray := [...]string{"a", "b", "c", "d", "e", "f", "g"}
       fmt.Println(sourceArray)
       slice_1 := sourceArray[:4]
       slice_2 := sourceArray[2:]
       slice_3 := sourceArray[1:]
       slice_4 := slice_3[1:]
       slice_5 := slice_4[1:]

       slice_1[3] = "i"       fmt.Println(sourceArray)
       fmt.Println(slice_1)
       fmt.Println(slice_2)
       fmt.Println(slice_3)
       fmt.Println(slice_4)
       fmt.Println(slice_5)

       wg := &sync.WaitGroup{}

       wg.Add(5)       /*slice, map, channel, function, method 皆為go語言中的引用型別; 一個切片slice)是一個隱藏陣列的引用,並且對於該切片的切片也是引用同一個陣列.*/       go func( p []string) {              defer wg.Done()
              p[3] = "w" //p[3] --> sourceArray[3]       }(slice_1) //在函式呼叫傳參時, 因為slice是引用型別, 所以slice_1與p這兩個引用皆指向同一個陣列sourceArray       go func( p []string) {              defer wg.Done()
              p[1] = "z" //p[1] --> sourceArray[3]       }(slice_2)       go func( p []string) {              defer wg.Done()
              p[2] = "x" //p[2] --> sourceArray[3]       }(slice_3)       go func(p []string) {              defer  wg.Done()
              p[1] = "q" //p[1] --> sourceArray[3] //即使切片的切片,依然指向同一個陣列sourceArray       }(slice_4)       go func(p []string) {              defer  wg.Done()
              p[0] = "t" //p[0] --> sourceArray[3] //即使切片的切片的切片等等,依然指向同一個陣列sourceArray       }(slice_5)

       wg.Wait()       /* 由以上分析可知, 在多執行緒程式設計, 協程程式設計中, 對於共享的變數,資源的讀寫是一定要序列化的, 比如加鎖,或通過channel來實現排他性訪問.       通過上面的測試程式碼,我們瞭解到,對於切片(slice)的讀寫最終都是對切片的隱藏陣列的讀寫, 如果讀寫的陣列索引範圍,或是元素重合, 則多個協程       讀寫共享元素,在併發情況下,則必然產生競爭,破壞共享元素資料, 所以要保護,要麼加鎖, 要麼用channel將訪問排隊序列化.       所以對於slice的使用一定要特別注意3點: (1)切片為引用型別 (2)一個切片是一個隱藏陣列的引用 (3)切片再切片等等,依然指向同一個隱藏陣列       注意: 這些結論都是在沒有超過切片容量(隱藏陣列的長度)的情況下得出的, 因為go語言內建函式append會在切片容量不夠時, 分配新的更大的切片及相應的隱藏陣列;       但是這並不影響以上結論,新切片, 舊切片依然各自指向自己的隱藏陣列.       */       fmt.Println("------")
       fmt.Println(slice_1)
       fmt.Println(slice_2)
       fmt.Println(slice_3)
       fmt.Println(slice_4)
       fmt.Println(slice_5)
}