內核線程(Kernel-Level Thread ,KLT)
十年專注建站、設計、互聯(lián)網產品定制制作服務,業(yè)務涵蓋成都品牌網站建設、商城開發(fā)、成都微信小程序、軟件系統(tǒng)開發(fā)、成都app軟件開發(fā)公司等。憑借多年豐富的經驗,我們會仔細了解每個客戶的需求而做出多方面的分析、設計、整合,為客戶設計出具風格及創(chuàng)意性的商業(yè)解決方案,創(chuàng)新互聯(lián)公司更提供一系列網站制作和網站推廣的服務,以推動各中小企業(yè)全面信息數(shù)字化,并利用創(chuàng)新技術幫助各行業(yè)提升企業(yè)形象和運營效率。
輕量級進程(Light Weight Process,LWP):輕量級進程就是我們通常意義上所講的線程,由于每個輕量級進程都由一個內核線程支持,因此只有先支持內核線程,才能有輕量級進程
用戶線程與系統(tǒng)線程一一對應,用戶線程執(zhí)行如lo操作的系統(tǒng)調用時,來回切換操作開銷相對比較大
多個用戶線程對應一個內核線程,當內核線程對應的一個用戶線程被阻塞掛起時候,其他用戶線程也阻塞不能執(zhí)行了。
多對多模型是可以充分利用多核CPU提升運行效能的
go線程模型包含三個概念:內核線程(M),goroutine(G),G的上下文環(huán)境(P);
GMP模型是goalng特有的。
P與M一般是一一對應的。P(上下文)管理著一組G(goroutine)掛載在M(內核線程)上運行,圖中左邊藍色為正在執(zhí)行狀態(tài)的goroutine,右邊為待執(zhí)行狀態(tài)的goroutiine隊列。P的數(shù)量由環(huán)境變量GOMAXPROCS的值或程序運行runtime.GOMAXPROCS()進行設置。
當一個os線程在執(zhí)行M1一個G1發(fā)生阻塞時,調度器讓M1拋棄P,等待G1返回,然后另起一個M2接收P來執(zhí)行剩下的goroutine隊列(G2、G3...),這是golang調度器厲害的地方,可以保證有足夠的線程來運行剩下所有的goroutine。
當G1結束后,M1會重新拿回P來完成,如果拿不到就丟到全局runqueue中,然后自己放到線程池或轉入休眠狀態(tài)。空閑的上下文P會周期性的檢查全局runqueue上的goroutine,并且執(zhí)行它。
另一種情況就是當有些P1太閑而其他P2很忙碌的時候,會從其他上下文P2拿一些G來執(zhí)行。
詳細可以翻看下方第一個參考鏈接,寫得真好。
最后用大佬的總結來做最后的收尾————
Go語言運行時,通過核心元素G,M,P 和 自己的調度器,實現(xiàn)了自己的并發(fā)線程模型。調度器通過對G,M,P的調度實現(xiàn)了兩級線程模型中操作系統(tǒng)內核之外的調度任務。整個調度過程中會在多種時機去觸發(fā)最核心的步驟 “一整輪調度”,而一整輪調度中最關鍵的部分在“全力查找可運行G”,它保證了M的高效運行(換句話說就是充分使用了計算機的物理資源),一整輪調度中還會涉及到M的啟用停止。最后別忘了,還有一個與Go程序生命周期相同的系統(tǒng)監(jiān)測任務來進行一些輔助性的工作。
淺析Golang的線程模型與調度器
Golang CSP并發(fā)模型
Golang線程模型
golang中最大協(xié)程數(shù)的限制
golang中有最大協(xié)程數(shù)的限制嗎?如果有的話,是通過什么參數(shù)控制呢?還是通過每個協(xié)程占用的資源計算?
通過channel控制協(xié)程數(shù)的就忽略吧。
以我的理解,計算機資源肯定是有限的,所以goroutine肯定也是有限制的,單純的goroutine,一開始每個占用4K內存,所以這里會受到內存使用量的限制,還有goroutine是通過系統(tǒng)線程來執(zhí)行的,golang默認最大的線程數(shù)是10000個??梢酝ㄟ^
來修改。但要注意線程和goroutine不是一一對應關系,理論上內存足夠大,而且goroutine不是計算密集型的話,可以開啟無限個goroutine。
部署簡單。Go編譯生成的是一個靜態(tài)可執(zhí)行文件,除了glibc外沒有其他外部依賴。這讓部署變得異常方便:目標機器上只需要一個基礎的系統(tǒng)和必要的管理、監(jiān)控工具,完全不需要操心應用所需的各種包、庫的依賴關系,大大減輕了維護的負擔。這和Python有著巨大的區(qū)別。由于歷史的原因,Python的部署工具生態(tài)相當混亂【比如setuptools,distutils,pip,
buildout的不同適用場合以及兼容性問題】。官方PyPI源又經常出問題,需要搭建私有鏡像,而維護這個鏡像又要花費不少時間和精力。
并發(fā)性好。Goroutine和channel使得編寫高并發(fā)的服務端軟件變得相當容易,很多情況下完全不需要考慮鎖機制以及由此帶來的各種問題。單個Go應用也能有效的利用多個CPU核,并行執(zhí)行的性能好。這和Python也是天壤之比。多線程和多進程的服務端程序編寫起來并不簡單,而且由于全局鎖GIL的原因,多線程的Python程序并不能有效利用多核,只能用多進程的方式部署;如果用標準庫里的multiprocessing包又會對監(jiān)控和管理造成不少的挑戰(zhàn)【我們用的supervisor管理進程,對fork支持不好】。部署Python應用的時候通常是每個CPU核部署一個應用,這會造成不少資源的浪費,比如假設某個Python應用啟動后需要占用100MB內存,而服務器有32個CPU核,那么留一個核給系統(tǒng)、運行31個應用副本就要浪費3GB的內存資源。
良好的語言設計。從學術的角度講Go語言其實非常平庸,不支持許多高級的語言特性;但從工程的角度講,Go的設計是非常優(yōu)秀的:規(guī)范足夠簡單靈活,有其他語言基礎的程序員都能迅速上手。更重要的是Go自帶完善的工具鏈,大大提高了團隊協(xié)作的一致性。比如gofmt自動排版Go代碼,很大程度上杜絕了不同人寫的代碼排版風格不一致的問題。把編輯器配置成在編輯存檔的時候自動運行gofmt,這樣在編寫代碼的時候可以隨意擺放位置,存檔的時候自動變成正確排版的代碼。此外還有gofix,
govet等非常有用的工具。
執(zhí)行性能好。雖然不如C和Java,但通常比原生Python應用還是高一個數(shù)量級的,適合編寫一些瓶頸業(yè)務。內存占用也非常省。
線程:
多線程是為了解決CPU利用率的問題,線程則是為了減少上下文切換時的開銷,進程和線程在Linux中沒有本質區(qū)別,最大的不同就是進程有自己獨立的內存空間,而線程是共享內存空間。
在進程切換時需要轉換內存地址空間,而線程切換沒有這個動作,所以線程切換比進程切換代價要小得多。
協(xié)程:
想要簡單,又要性能高,協(xié)程就可以達到我們的目的,它是用戶視角的一種抽象,操作系統(tǒng)并沒有這個概念,主要思想是在用戶態(tài)實現(xiàn)調度算法,用少量線程完成大量任務的調度。
Goroutine是GO語言實現(xiàn)的協(xié)程,其特點是在語言層面就支持,使用起來十分方便,它的核心是MPG調度模型:M即內核線程;P即處理器,用來執(zhí)行Goroutine,它維護了本地可運行隊列;G即Goroutine,代碼和數(shù)據(jù)結構;S及調度器,維護M和P的信息。