寫了一年golang,來聊聊程序 執行緒與協程

2022-09-19 03:39:11 字數 2467 閱讀 3004

本文已收錄 歡迎star。

在早期的單任務計算機中,使用者一次只能提交乙個作業,獨享系統的全部資源,同時也只能幹一件事情。進行計算時不能進行 io 讀寫,但 cpu 與 io 的速度存在巨大差異,乙個作業在 cpu 上所花費的時間非常少,大部分時間在等待 io。

為了更合理的利用 cpu 資源,把記憶體劃分為多塊,不同程式使用各自的記憶體空間互不干擾,這裡單獨的程式就是乙個程序,cpu 可以在多個程序之間切換執行,讓 cpu 的利用率變高。

為了實現 cpu 在多個程序之間切換,需要儲存程序的上下文(如程式計數器、棧、核心資料結構等等),以便下次切換回來可以恢復執行。還需要一種排程演算法,linux 中採用了基於時間片和優先順序的完全公平排程演算法。

多程序的出現是為了解決 cpu 利用率的問題,那為什麼還需要執行緒?答案是為了減少上下文切換時的開銷

程序在如下兩個時間點可能會讓出 cpu,進行 cpu 切換:

而程序切換 cpu 時需要進行這兩步:

程序和執行緒在 linux 中沒有本質區別,他們最大的不同就是程序有自己獨立的記憶體空間,而執行緒(同程序中)是共享記憶體空間。

在程序切換時需要轉換記憶體位址空間,而執行緒切換沒有這個動作,所以執行緒切換比程序切換代價更小。

為什麼記憶體位址空間轉換這麼慢?linux 實現中,每個程序的位址空間都是虛擬的,虛擬位址空間轉換到實體地址空間需要查頁表,這個查詢是很慢的過程,因此會用一種叫做 tlb 的 cache 來加速,當程序切換後,tlb 也隨之失效了,所以會變慢。

綜上,執行緒是為了降低程序切換過程中的開銷。

當我們的程式是 io 密集型時(如 web 伺服器、閘道器等),為了追求高吞吐,有兩種思路:

為每個請求開乙個執行緒處理,為了降低執行緒的建立開銷,可以使用執行緒池技術,理論上線程池越大,則吞吐越高,但執行緒池越大,cpu 花在切換上的開銷也越大

執行緒的建立、銷毀都需要呼叫系統呼叫,每次請求都建立,高併發下開銷就顯得很大,而且執行緒占用記憶體是 mb 級別,數量不能太多

為什麼執行緒越多 cpu 切換越多?準確來說是可執行的執行緒越多,cpu 切換越多,因為作業系統的排程要保證絕對公平,有可執行執行緒時,一定是要雨露均沾,所以切換次數變多

使用非同步非阻塞的開發模型,用乙個程序或執行緒接收請求,然後通過 io 多路復用讓程序或執行緒不阻塞,省去上下文切換的開銷

這兩個方案,優缺點都很明顯,方案1實現簡單,但效能不高;方案2效能非常好,但實現起來複雜。有沒有介於這兩者之間的方案?既要簡單,又要效能高,協程就解決了這個問題。

協程是使用者視角的一種抽象,作業系統並沒有這個概念,其主要思想是在使用者態實現排程演算法,用少量執行緒完成大量任務的排程。

協程需要解決執行緒遇到的幾個問題:

第一點好實現,使用者態的協程,只是乙個資料結構,無需系統呼叫,而且可以設計的很小,達到 kb 級別。

第二點只能減少上下文切換次數來解決,因為協程的本質還是執行緒,其切換開銷在使用者態是無法降低的,只能通過降低切換次數來達到總體上開銷的減少,可以有如下手段:

讓可執行的執行緒盡量少,這樣切換次數必然會少

讓執行緒盡可能的處於執行狀態,而不是阻塞讓出時間片

goroutine 是 golang 實現的協程,其特點是在語言層面就支援,使用起來非常方便,它的核心是mpg排程模型:

除此之外還有乙個全域性可執行佇列。

在 golang 中使用go關鍵字啟動乙個 goroutine,它將會被掛到 p 的 runqueue 中,等待被排程

2. 當 m0 中正在執行的 g0 阻塞時(如執行了乙個系統呼叫),此時 m0 會休眠,它將放棄掛載的 p0,以便被其他 m 排程到

3. 當 m0 系統呼叫結束後,會嘗試「偷」乙個 p,如果不成功,m0 將 g0 放到全域性的 runqueue 中

p 會定期檢查全域性 runqueue,保證自己消化完 g 後有事可做,同時也會從其他 p 裡「偷」 g

從上述看來,mpg 模型似乎只限制了同時執行的執行緒數,但上下文切換只發生在可執行的執行緒上,應該是有一定的作用,當然這只是一部分。

golang 在 runtime 層面攔截了可能導致執行緒阻塞的情況,並針對性優化,他們可分為兩類:

所以綜合來看,goroutine 會比執行緒切換開銷少。

從單程序到多程序提高了 cpu 利用率;從程序到執行緒,降低了上下文切換的開銷;從執行緒到協程,進一步降低了上下文切換的開銷,使得高併發的服務可以使用簡單的**寫出來,技術的每一步發展都是為了解決實際問題。

又是一年跳槽季,我們來聊聊面試吧

剛過完年,由於部門有同事離職,而且今年的工作規劃任務量更大,難度也公升級了,因此找 商推一些效能測試的同學來面試。前後看了有一百多份簡歷,篩選了二十幾份通知面試。這裡,針對面試過程中發生的一些事以及個人之前的面試經驗,再來聊聊面試吧,這次,從面試官的角度來說。之前也寫了幾篇關於面試的部落格,下面是傳...

測試面試 又是一年跳槽季,我們來聊聊面試吧

剛過完年,由於部門有同事離職,而且今年的工作規劃任務量更大,難度也公升級了,因此找 商推一些效能測試的同學來面試。前後看了有一百多份簡歷,篩選了二十幾份通知面試。這裡,針對面試過程中發生的一些事以及個人之前的面試經驗,再來聊聊面試吧,這次,從面試官的角度來說。之前也寫了幾篇關於面試的部落格,下面是傳...

畢業一年來的感觸

準確的說,離開學校已經一年又近四個月了,而這十六個月過的又是這麼的匆忙,感覺好多東西匆匆而過,卻沒有留下任何的痕跡,而留下的僅僅是寫過的幾行 整體的感受是,累。這一年來值得回憶的是那麼的稀少,現在能夠回想起來的大部分是自己負責的專案的主頁面。一種厭倦的感覺,但又捨不得放棄。好吧,從開始寫日誌到目前為...