iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Software Development

30天學會Golang系列 第 17

Day17 - Go的 Select

  • 分享至 

  • xImage
  •  

Select

select 的語法與 switch 很相似,但與 switch 不同的是,select 中雖然也是使用 case,但在 select 中的 case 必须都是 Channel 的收發操作。可以參考下圖片,來自 參考來源2,裡面有介紹大量的原始碼,有興趣可以看看,然後下面有個簡單例子:

https://ithelp.ithome.com.tw/upload/images/20220928/20150797oV6f6Dxsah.png

func generator() chan int {
	out := make(chan int)
	go func() {
		i := 0
		for {
			out <- i
			i++
		}
	}()
	return out
}

func main() {
	var c1, c2 = generator(), generator()

	for {
		select {
		case n := <-c1:
			fmt.Println("Received from c1:", n)
		case n := <-c2:
			fmt.Println("Received from c2:", n)
		}
	}
}

輸出結果為:

Received from c2: 30322
Received from c2: 30323
Received from c2: 30324
Received from c2: 30325
Received from c1: 27700
Received from c2: 30326
Received from c2: 30327
Received from c2: 30328
Received from c2: 30329
Received from c1: 27701
Received from c2: 30330
Received from c2: 30331

在生成的過程中可以看到 c1 與 c2 是一個一個值丟出來的 ,這是 channel 阻塞式的特性,channel 必須先收到資料,然後再丟出資料,之後才能再收下一筆資料,那假設我們想利用 channel 做一個非阻塞式的,我們在 select 的 case 中多加入一個 default 的就可以達到這個效果,程式碼如下

// func generator() chan int {
// 	out := make(chan int)
// 	go func() {
// 		i := 0
// 		for {
// 			out <- i
// 			i++
// 		}
// 	}()
// 	return out
// }

// func main() {
// 	var c1, c2 = generator(), generator()

// 	for {
// 		select {
// 		case n := <-c1:
// 			fmt.Println("Received from c1:", n)
// 		case n := <-c2:
// 			fmt.Println("Received from c2:", n)
		default:
			fmt.Println("No value received")
// 		}
// 	}
// }

輸出結果為:

Received from c2: 5462
Received from c1: 5177
Received from c2: 5463
Received from c1: 5178
No value received
No value received
No value received
No value received
No value received
No value received
Received from c2: 5464
No value received
Received from c1: 5179
No value received
No value received
No value received
Received from c2: 5465
Received from c1: 5180

default 在這裡的功能就是當 channel 中的數據丟出去之後,此時 channel 中的值會變成 nil,那這時 default 就會隨著 goroutine 的速度瘋狂的輸出,也就變成了非阻塞式的結構。如果我們希望他能在搜集一定的時間後停止,我們可以在 select 裡面多加一個判斷情況,以下面例子來說,就是3秒後會停止運行

// func generator() chan int {
// 	out := make(chan int)
// 	go func() {
// 		i := 0
// 		for {
// 			out <- i
// 			i++
// 		}
// 	}()
// 	return out
// }

// func main() {
// 	var c1, c2 = generator(), generator()
	tm := time.After(3 * time.Second)

// 	for {
// 		select {
// 		case n := <-c1:
// 			fmt.Println("Received from c1:", n)
// 		case n := <-c2:
// 			fmt.Println("Received from c2:", n)
		case <-tm:
			return
// 		default:
// 			fmt.Println("No value received")
// 		}
// 	}
// }
第17天報到,我對於 goroutine 與 channel 的運用還是很不清楚,從 day12~17 大家先暫時擱著,目前我還真的沒弄懂,以後弄懂後再回來改關於 goroutine 的所有講解,目前滿滿的困惑@@,我很抱歉。先記錄一下我腦中的一個例子,目前還沒有能力解決,利用 goroutine 與 channel 搜集一個資料庫的資料,有沒有辦法在不使用 waitgroup 的情況下,能告知資料庫的資料全部搜集完,然後對這些資料做後續處理。
比方說今天有一個 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 有發揮到功能

如果有大大知道怎麼做,還請不吝賜教。

參考來源

  1. https://coding.m.imooc.com/classindex.html?cid=180
  2. https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-select/

代碼連結

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


上一篇
Day16 - Go的 Mutex (互斥鎖)
下一篇
Day18 - Go的 package
系列文
30天學會Golang31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言