iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
1
自我挑戰組

Let's Eat GO ! 實務開發雜談by Golang系列 第 9

Day9 .[正確資料篇] range、map、slice、channel、goroutine 的組合應用

  • 分享至 

  • xImage
  •  

這篇講述會比較片段一點,一些Golang常用的資料結構,使用上的小細節。

range 搭配 map、slice

range 是Golang做interation的關鍵字,基本上與for迴圈做配合。

range for

	var list []string
	list = append(list, "1", "2", "3")
	for _, v := range list {
		log.Println(v)
	}

range map

    exMap := make(map[int]string)
	exMap[1] = "a"
	exMap[2] = "b"
	exMap[3] = "c"
	for _, v := range exMap {
		log.Println(v)
	}

range map 與range slice有個差異,對於slice,內容的元素會照順序一個一個挑出來,做了100次range slice,100次挑出內容元素的順序都是一樣。

而range map 就是亂數挑選出內容的元素了,同樣的資料做range 100次可能出來的順序都不同,所以若有講究range 內容元素出現的順序,map就是一個很不適合的設計方式。

range 搭配 channel

range channel 又是另外一回事,千萬不要用range slice或者range map的概念去想range channel,對於Golang來說range channel是一個特別的組合技,有興趣細究可以參考Go by Example上面的說明。

range-over-channels

worker-pools

下面是一部分worker pool 的程式碼

jobs := make(chan int, 100)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

range 並不會因為把jobs的內容元素挑完,就離開這段程式,概念不是這樣子去想。

jobs 是一個buffer channel,channel的內容什麼時候可以挑完?或者挑的時候沒東西就作罷嗎?

不不不,要確保channel沒東西只有在channel被關閉,或者發生deadlock的時候,所以反推來想,在buffer channel 未被關閉之前,這個range channel 會一直卡在那邊,一但有資料可以從channel拿出來,則執行for迴圈內部的程式碼,然後繼續等待著從下個可以從channel拿出東西的時機。

上述的特性,就可以有很多應用,可以想像一下,上述程式碼的worker method,把他看做創建了某個工人,這個工人會一直stand by 在那邊,等待工作,一但有工作丟給他,他會立即知道,並且開始做事,乖乖的做完工作(for迴圈內的程式碼),才會再看channel能不能拿到新工作,一有新工作立即接手出來做事,持續這樣的循環。

channel、goroutine 的搭配組合,造就輕鬆使用worker pool

配合goroutine的使用,如果用goroutine的方式創建這些工人,要幾個工人就有幾個工人,(要記得注意系統資源和leak問題),也因為是開goroutine,程式碼就會往下繼續執行,換句話說,你這個工頭就可以專注繼續做自己的事情。

若有需要做的工作,就將工作丟進排隊的序列,也就是上面jobs這個buffer channel就好,因為channel的特性,丟進去的工作會依序開始處理,而會分到哪個工人身上,是隨機分配,但是有正在做工作的工人就不會去拿新工作,剩下有空閒的工人就有機會拿到尚未處理的工作,就是worker pools的原理。

以上大概是一種工人先找好,確定好工作人數,再發配工作執行的概念,工人一直stand by 在那邊,你隨時要丟工作過去都可以,丟了多少也都可以,每個工人都會做事,做完了立刻去拿新的來做,直到拿不到工作,就回到stand by,而且每個人的工作量基本上很公平。這個機制運用在控管連線上非常有幫助,因為我們希望連線的數量是穩定控制的,不會突然爆多,或者沒有充分使用到資源。

結合重複利用的概念,你的程式會更有效率

worker pools的概念,與前面爆連線那篇有些相似,重複利用就能節省資源。

使用worker pools的設計,一開始建立出數個工人,工人固定stand by,有工作他們就做,換句話說後續你也就不會重複一直建新的工人出來。

是否有想過建立出工人,這也是要花系統資源的,開goroutine雖然成本便宜,但終究也是需要一定資源,再者就好比現實社會,你能夠請多少個工人,也要看你的能力所及,看你能發多少薪水,系統並不是可以無限制地開出goroutine。

而開一個goroutine,指定做某項工作,做完就消失,但後續可能又有類似的工作進來,又重新開goroutine,做完又消失,這就是一種很嚴重的浪費,如果允許的話,重複利用不是更好呢?

Golang 的FAQ開宗明義,有些設計就是為了快而誕生的,本身這部分底子好,我們除了便利地使用這些Golang準備好的東西,再加上正確的程式架構設計觀念,能夠發揮出更加的效能優勢。


上一篇
Day8 .[正確資料篇] slice 與 map 加lock
下一篇
Day10 .[正確資料篇] 浮點數運算請decimal package協助
系列文
Let's Eat GO ! 實務開發雜談by Golang30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言