當時在 day17 對 channel 充滿疑惑,尤其是聽到下面這句話:
不要通過共享内存来通信,我们應該使用通信来共享内存
只能說我是有看沒有懂,所以為自己出了一個小題目,如下面所示:
有一個 10 * 10 的 array1,裡面的資料結構如下
array[0] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
array[1] = [1, 1, 2, 3, 4, 5, 6, 7, 8, 9]
array[2] = [2, 1, 2, 3, 4, 5, 6, 7, 8, 9]
然後開 10 個 channel,等到每個 channel 的資料蒐集完後
對這些資料統一都加 1,存至新的 array2 中,此時array2 的順序是混亂
這樣才能確保 goroutine 有發揮到功能
一般的文章都會提到 Mutex 與 WaitGroup,當然如果透過 WaitGroup 可以比較直覺的處理,可是如果以分類來說, WaitGroup 與 Mutex 都是屬於傳統的處理方式,同時也是 "透過共享內存來通信",就沒有用到身為 go 工程師希望的 "透過通信來共享內存",而 channel 就是使用 "通信來共享內存",可是當時怎麼用 channel 做就是會不曉得要怎麼完成這個問題,但隨著一步步的了解,不能說真的懂,只能說好像比較有感覺,今天就是要來處理這個問題,至於為什麼要用 "通信來共享內存",可以參考 參考來源1,講的我目前也只是有感覺,但還不能說真懂,等以後真的需要用到這個的時候可能就會比較有感覺了吧:)
不過今天我們先來個暖身題,明天再來解決上面的問題,如果有興趣的人也可以透過今天這篇的內容去做看看上面那個問題
首先我們先將題目稍做變化一下,改成以下情況:
有一個 10 * 10 的 array1,裡面的資料結構如下
array[0] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
array[1] = [1, 1, 2, 3, 4, 5, 6, 7, 8, 9]
array[2] = [2, 1, 2, 3, 4, 5, 6, 7, 8, 9]
然後開 10 個 channel
計算每列資訊的總和
看看是否結果的順序是混亂的
這樣才能確保 goroutine 有發揮到功能
首先我們先創建 2 維陣列:
// 創建 2 維矩陣
func create2DimArray(m, n int) [][]int {
arr := make([][]int, m)
for i := 0; i < m; i++ {
arr[i] = make([]int, n)
// 裡面塞值,來確認是哪一個 row 的資料
for j := 0; j < n; j++ {
if j == 0 {
arr[i][j] = i
} else {
arr[i][j] = j
}
}
}
return arr
}
// 顯示 2 維陣列的內容物
func show2DimArray(arr [][]int) {
for i := range arr {
for j := range arr[i] {
fmt.Printf("%d ", arr[i][j])
}
fmt.Println()
}
}
func main() {
m, n := 10, 10
arr := create2DimArray(m, n)
showTitle("=== 顯示 2 維 arr 內容物 ===")
show2DimArray(arr)
}
輸出結果為:
=== 顯示 2 維 arr 內容物 ===
0 1 2 3 4 5 6 7 8 9
1 1 2 3 4 5 6 7 8 9
2 1 2 3 4 5 6 7 8 9
3 1 2 3 4 5 6 7 8 9
4 1 2 3 4 5 6 7 8 9
5 1 2 3 4 5 6 7 8 9
6 1 2 3 4 5 6 7 8 9
7 1 2 3 4 5 6 7 8 9
8 1 2 3 4 5 6 7 8 9
9 1 2 3 4 5 6 7 8 9
接下來就是開啟 goroutine 與 channel 的時間:
// 總和 1 個 row 的值
func sum1Row(arr1Dim []int, c chan int) {
sum := 0
for _, v := range arr1Dim {
sum += v
}
c <- sum // 總和後傳給 channel
}
// 總和 1 個 row 的值 與 顯示
func sum1RowAndShow(arr [][]int, m, n int) {
c := make(chan int)
// 併發讀取與寫入
for i := 0; i < m; i++ {
go sum1Row(arr[i], c)
}
// 這時候只有 c,就可以正常併發讀取
for i := 0; i < m; i++ {
fmt.Printf("%d ", <-c)
fmt.Println()
}
}
//func main() {
// m, n := 10, 10
// arr := create2DimArray(m, n)
// showTitle("=== 顯示 2 維 arr 內容物 ===")
// show2DimArray(arr)
showTitle("=== goroutine 對 2 維每 row 總和 與 輸出結果 ===")
sum1RowAndShow(arr, m, n)
//}
上面 func sum1Row(arr1Dim []int, c chan int) 中的 c <- sum 就是要將資料塞進 goroutine 中的 channel,在 func sum1RowAndShow(arr [][]int, m, n int) 的最下面的 <-c 除了讓資料顯示出來以外,還有一個很重要的功能,就是為了要讓 main 所在的執行序等到全部蒐集完才能結束,這點很重要!!等於是取代了 waitgroup 與 time.sleep 的功能,接下來就可以看看下面的結果:
=== goroutine 對 2 維每 row 總和 與 輸出結果 ===
45
49
46
47
48
51
50
52
53
54
接下來明天最後一天就來解最上面的那個例子
https://github.com/luckyuho/ithome30-golang/tree/main/day29