Golang語言切片slice的執行緒協程安全問題
阿新 • • 發佈:2022-05-04
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) }