iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Software Development

30天學會Golang系列 第 29

Day29 - Go的 channel day17 的還債 (上)

  • 分享至 

  • xImage
  •  

channel 二度理解

當時在 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 

接下來明天最後一天就來解最上面的那個例子

第29天報到,終於好像有比較懂 channel,明天就來把從第 17 天困惑到今天的題目解了:)

參考來源

  1. https://draveness.me/whys-the-design-communication-shared-memory/

代碼連結

https://github.com/luckyuho/ithome30-golang/tree/main/day29


上一篇
Day28 - Go的 jwt 實作
下一篇
Day30 - Go的 channel day17 的還債 (下)
系列文
30天學會Golang31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言