iT邦幫忙

0

筆記:Golang Channel 隨筆

  • 分享至 

  • xImage
  •  

筆記:Golang Channel 隨筆

想特別寫個筆記來讚嘆一下 golang unbuffered channel 的記憶體處理設計~~

Unbuffered Channel

當我們用語法 make(chan int) 來建立一個 channel 時,預設會是一個 unbuffered channel,也就是會阻塞(block) 的 channel。

這在非同步的程式設計裡,聽起來似乎是怪怪的,但 golang 卻在記憶體做到了很聰明的設計。

舉個簡單的例子,假設有一個 api 專門收請求,收到後會把資料直接丟進 channel 裡。以非同步的設計概念,在丟進 channel 之後,這個請求的 thread(or goroutine) 也就是 producer 就會趕快回覆,然後繼續去等下一次請求。但是 unbuffered channel 會把這個 producer 堵在那裡,它一定要等到有一個 consumer 來跟它手把手把資料拿走之後, producer 才會被釋放,然後繼續去處理下一個的請求。

聰明的點在哪裡?

就在它的記憶體處理的方式。

不管是 「.net channel 物件」 or 「golang buffered channel」,這個打進來的資料過程,都會是先從 producer 身上複製一份到 heap 上,然後再從 heap 複製到 consumer 的 stack 上。所以他的順序正常會是 Thread A Stack=> heap => Thread B Stack

但是 golang 的 unbuffered channel 卻是設計成 Thread A Stack => Thread B Stack,中間直接跳過 heap。也就是這筆資料一直都在 producer 的 stack 上,然後等 consumer 一來,用 stack to stack memory copy 的方式直接把資料給到 consumer 的 stack 上。所以在 golang unbuffered 裡,資料的流動是不需要進到 heap 裡的(個人覺得讚嘆)。

這個用簡單的數學來算,golang unbuffered 的實作就是少了一個動作。在邏輯裡,只要你能少一個動作,當數量被放大到很大的時候,這裡就會有明顯的差距出來了,不管這個動作的 effort 有多


Buffered Channel

既然 unbuffered 聽起來那麼神,那 buffered channel 還有使用的需要嗎?當然這也就是看情境了。再好用的工具也是要放對位置才會好用。

如果有些場景,就是一定要預留一些緩衝給 producer,也許就是會擔心量突然衝上來,consume 太慢,那 buffered channel 就還是要使用。總比請求完全被回堵的好。用空間來買一些系統的穩定跟安全。

只是 buffered 的數量要調整多少就看情況了,畢竟數量設太小,當滿了的時候也是會遇到阻塞的情況。


阻塞 for 不那麼重要的資料

設計上,如果有些資料不那麼重要,突然量衝上來然後掉了一些也無所謂(像監控 log 這秒掉了,下秒資料再出來也能接受),對系統運作也沒影響,這時候也可以考慮搭配 select 語法來使用。

底下寫法可以讓 channel 不管是 unbuffered or buffered,只要搭配 select 語法,當 channel 就是堵住的時候,可以直接走到 default,那條路,讓程式繼續往下,只是會放棄這筆資料。

select {
case ch <- data:
    log.Println("enqueue")
default:
    log.Println("drop")
}

心得

buffered channel or unbuffered channel 要使用哪個本身就是一種 trade-off 了。雖然我個人覺得 unbuffered channel 的記憶體處理真的讓人蠻驚豔的,但也不可能全部的場景它都適合(就像化妝品一樣,還是有分日霜、晚霜、隔離霜……)。

總歸還是要看整體的系統架構自己想要如何來設計跟實作。這篇文章純屬記錄一下,對 golang 的 unbuffered channel 的讚嘆~~


參考資料

go in action


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言