文章也同時發表於medium(`・ω・´)”
稍微提一下,以下所有圖畫都是我妹妹幫忙畫的,希望有幫助大家~
髒沙發LineBot在開發時曾經碰過一個問題,就是在處理大圖的時候,有兩個步驟,分別是
而在上篇文章有介紹Node.js對於IO操作是不會阻塞的,CPU操作是會阻塞的,這是由於Node.js是single thread,沒有multi thread的幫忙來處理CPU-bound。
在Golang方面,由於goroutine擁有操作多個thread的能力,所以可以讓每個thread來分工CPU操作。
Golang處理的方法遵照MPG模型來處理這類的行為,但如果直接討論可人會讓人一臉矇逼。我們可以介紹「行程到現在高併發的協程」來慢慢了解「出現什麼問題,可用什麼方法應對」,這樣應該會比較好了解。
我們可先有一個CPU核心運作的概念,以單純的方式來討論問題,「單一個CPU核心,多執行緒」的運作方式如下
單CPU核心會切成許多的時間切片(timeslicing),一下做thread A的事情,一下做thread B事情,而當做得非常快,就像用來「一個人執行多件事」,舉個最近很好看的音速小子來當例子
單CPU核心就是音速小子,因為做事太快,所以同時做切菜、拖地、與蛋頭博士泡茶,在一般人的感知裡就像同時做這三件事情
有了這個概念後,我們就可以開始討論
行程是「資源和獨立運行的最小單位」,而在以前,因為還沒有「multi thread的概念」,所以行程同時也是「執行的最小單位」,這導致一個問題
在切換thread A或thread B的事情時,開銷變得很大
我們稱這個切換行為為context switch,當音速小子切菜時身上需要有菜刀、大蔥、胡蘿波,之後拖地又要準備拖把、水桶,再來要泡茶還要準備茶杯,在這每次的切換工作時也要「切換資源」,導致切換的成本變很大,那這要怎麼解決呢?
於是就有了線程的概念。
線程這個概念的出現取代了行程「執行的最小單位」的位置,所以要處理多件事情時,可以不需開多個行程,而是「一個行程多個線程」,即
音速小子把這三件事情所需的各個資源都帶在身上,這樣切換工作時就不用再拿菜刀換拖把了
一切都很美好,直到「周邊IO-瓦斯爐」滾茶讓音速小子乾等了許久。
由於時間切片是固定的,每次做事的時間也是固定的,這導致音速小子在切換到泡茶這件事時,一直在等周邊的瓦斯爐把茶滾好,所以一直看著茶發呆,這樣等於白白浪費了這些時間
是否能在滾茶時,讓thread的事情從滾茶換成擦茶桌呢?
可以,這時我們可以在程式碼這樣寫
go 滾茶() //這行不會等他完成即會往下一行跑
擦完茶桌後,再等茶滾好()
這樣音速小子就可以更充分利用時間。
你可能有注意到,我們在程式碼裡面做了跟「單一個CPU核心,多執行緒」類似的行為,即是「切換事情」。
值得注意的是,
協程的切換是由程式碼完成的,而單一個CPU核心線程的切換是由時間切片固定分配的。
這代表協程更加的輕量,因為比起線程它切換的開銷更小了,因為它是在原thread裡面做切換,不像多線程還要跨thread來切換。
大家會發現,單核心始終是一個人,所以很快速切換處理事情的時間,其實同等於認真處理一件一件事情的時間
甚至在快速切換時,因為多了這個切換的開銷,往往會比認真一件一件處理來得更久
所以這時就出現了Parallel(平行)這個概念,即是多核心,大家可以看到由於核心的增加可以讓同一時間可以處理更多thread
多核心的世界裡,需要考慮的事情比單核心來得更多,這些核心要怎麼分配thread就變成了一門學問,大家要先有一個觀念,就是thread分別有
現在有查爾斯與音速小子兩個核心,現在他們一樣要做切菜、拖地、與蛋頭博士泡茶這三件事情,他們一樣非常快速的做這些事,但三件事情給兩個人做勢必會遇到「分配」上的問題,所以有以下方法來分配事情。
講得有點抽象,以下配合幾個角色,我們用圖來解釋
大家可以發現,原本的行程為什麼後來需要線程、內核線程、用戶線程、協程,其實最大的目標不外乎就是
所以統整下來,可以將行程、線程、協程的關係畫張廣義的示意圖
簡單的來說就是依照不同的情境,以不同的層次來分配他們事情
稍微介紹完這些概念後,接下來會介紹Golang的MPG模型,謝謝你的閱讀ヽ(・ω・ゞ)
如有錯誤歡迎勘正指教,謝謝你的閱讀~
感謝~
已修改名稱,關於翻譯的名詞要使用哪個,其實我思考很久,
一個英文單字有很多種翻法,
所以文章中如果有提到的專業名詞,我會盡量把英文名詞一起寫上,
希望能更明確表達意思◝(・ω・)◟
可以參考微軟的對照表:https://www.microsoft.com/en-us/language
Node.js對於IO操作是不會阻塞的,CPU操作是會阻塞的,這是由於Node.js是single thread,沒有multi thread的幫忙來處理CPU-bound。
nodejs 已經有 worker thread 了唷
以前也有 child_process
跟 cluster
可以用
你好~
感謝你的回覆,
坦白說我一直在思考要不要把work thread
, child_process
, cluster
納入,
因為work thread
是新的特性,
而child_process
, cluster
又稍稍是比較特別的做法,
怕寫入文章會造成主題發散,但看到你的留言後又覺得如果不寫進去,
也可能會造成觀念上的錯誤,
所以,我之後會把這些概念評估在文章裡,再次感謝你的回覆~
我是覺得稍微提一下就好
畢竟你的主題是 Go
我會提這個是因為每個在跟 nodejs 做比較的文章
幾乎都是以我引用你的那段話來做起點
很多人看完就覺得 nodejs 就是完全拿 CPU-intensive 的 task
一點辦法都沒有
行程、線程、協程
大陸講法為進程/線程/協程
是的你說的沒錯,或許以後都用英文來說明這些名詞會比較單純些XD
中英文對照.謝謝...
你寫的文章很棒.謝謝