




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
并發概念(計算機)
一、串行程序、并發程序、并行程序
串行程序:指只能被順序執行的指令列表。
并發程序:并發程序屬于程序,其內部是由多個部分(串行程序)組
合在一起,這些部分組合在一起構成了一個整體,叫做并發程序。也
叫單元
并行程序:指可以在并行的硬件上(CPU,服務器),運行的并發程
序。
二、并發系統、并行系統
并發系統:程序和程序之間是通過協議協商一致后形成通信。并發系
統就是多個并發程序間構成的,看作是一個系統。
并行系統:指并發系統以并行的方式存在。每個并發系統中的程
序,很有可能會部署在多個并行硬件上(服務器)同時運行,也叫做
分布式系統。
三、并發程序內部交互
1.什么是并發程序的內部交互?
并發程序中存在多個串行程序,這些串行程序之間可能會存在數據交
互的需求。比如多個串行程序對一個共享資源進行訪問(數據庫,消
息隊列);又或者在它們之間傳遞一些數據。
在這些需求的環境下,協調它們的執行,就要涉及到同步。
2.同步的作用
避免在并發訪問共享資源時發生的沖突,可以有條不紊的傳遞數據。
3.同步的原則
當程序要訪問一個共享資源時,就必須請求該共享資源并獲取對應的
訪問權;反之如果程序不再需要一個共享資源,就要放棄該資源的訪
問權。(釋放資源,Close())
而應該等到其釋放資源后在進行訪問(鎖)。結論:一個共享資源在
同一時刻內,只能被一個程序占用。
同步會導致系統資源的浪費,耦合性太強。所以有了異步通信的思想。
4.異步通信
發送方可以不加延遲的將消息發送出去,接收方也不會造成發送方的
等待。(解耦)
數據會被臨時存儲在一個通道緩存(IPC)的通道中。通道緩存是一
種特殊的共享資源,可以被多個程序使用。接收方準備就緒后無需知
道發送方,直接從通道緩存中獲取數據。
多線程編程概念(計算機)
一、什么是多線程編程
POSIX:可移植性的Unix操作系統接口。
NPTL:Linux操作系統的最新線程庫。
多線程編程是一種比多進程編程更靈活,更高效的編程方法。在
Linux系統中提供了一個以POSIX為標準定義的線程(簡稱:POSIX
線程)為中心的各種系統調用。POSIX也是go并發編程在linux系
統下真正使用的內核接口,也就是NPTL本地POSIX線程庫。
二、什么是線程
線程可以視為進程中的控制流,一個進程中至少要包含一個線程。
因為一個進程中至少要一個控制流持續運行。所以第一個線程會隨著
其進程的啟動而創建,這個線程也被稱之為主線程。
一個進程中可以存在多個線程,這些線程是由當前進程中存在的線
程創建出來的,而創建的方法就是系統調用。更準確的說是調用
pthread_create函數。
擁有多個線程的進程可以并發執行多個任務,并且即使某個線程被
阻塞也不會影響到進程中其它任務的執行。線程會大大提升程序的響
應效率和吞吐量
線程不能獨立于進程之上,線程的生命周期不能超過其所在進程的
生命周期。
三、如何實現一個線程
通過系統調用來及制線程。線程與進程的父子家族關系樹結構不
同,線程之間的關系都是平等的。它們之間不存在層級關系,任何線
程都可以對同一進程中的其它線程進行有效的管理。其中管理分為5
種:
pthread_create創建線程
pthread_cancel終止線程
pthreadjoin連接已經終止線程
pthread_detach分離線程
pthread_exit徹底退出主線程
3-1V線程TID標識
和進程一樣,線程也有屬于自己的ID,叫做TID。但與進程不同
的是,線程ID在系統內中并不唯一,只會在其當前進程下唯一。不
過linux系統則確保了每個線程在系統內ID的唯一性。且當某個線
程不復存在后,其TID可以被其它線程復用。線程ID由操作系統內
核進行分配和維護。
四、線程運行時發生異常會怎么樣
線程異常后會強制調用pthread.cancel函數來終止線程,然后
調用detach函數分離線程。根據不同的異常情況會進入僵死狀態和
終止狀態兩種。
僵死狀態就是還會保留一些程序所需的資源,不會刪掉線程上的
全部數據。如果不再需要僵死線程,就會調用pthreadJoin終止掉
僵死線程。
五、如何在兩個線程間共享數據
一個進程中的所有線程都擁有自己的線程棧,并以此來存儲線程私
有的數據棧。這些線程的線程棧包含在其所屬進程的虛擬內存空間當
中。
一個進程中的很多資源都會被其所有線程共享,這些被線程共享資
源包括(在當前進程的虛擬內存中存儲的):數據段、代碼段、堆、
棧、信號,以及當前進程支持的文件描述符。
正因如此,同一個進程中的多個線程運行的肯定是一個程序。只不
過具體的流控方式會有所不同。另外,創建一個線程也不會像創建進
程那樣費勁,因為線程需要的所有代碼、數據段、資源都在其進程中
存儲不需要被復制就能使用。
操作系統內核提供了若干系統調用以便應用程序能夠管理當前進
程中的所有線程,還可以通過相應的系統功能協調這些線程的運行。
六、線程狀態
線程狀態分為:
1.就緒狀態
2.運行狀態
3.阻塞狀態
4.睡眠狀態
當調用pthread_create函數時線程會進入就緒狀態。當線程獲
得運行時機被CPU激活運行之后會進入運行狀態。
在線程運行中如果出現了阻塞等待,就會進入睡眠狀態,阻塞解
除后會重新進入就緒狀態。
在線程運行中如果出現了return語句,或者退出線程的語句,根
據當時不同的線程執行環境會進入僵尸狀態和終止狀態。
不管是僵尸狀態還是終止狀態最終都會被回收。
七、線程調度
在線程的生命周期中,操作系統內核對線程的調用是非常核心的部
分。正是因為有了調度器的實時調度和切換,才給眾多線程一種并行
運行的幻覺。調度器會把事件劃分成極小的時間片,并把這些時間片
分配給不同的線程,以使眾多線程都有機會在CPU上運行。一個線
程什么時候能夠獲得CPU時間,以及在CPU上運行多久,都是調
度器的工作范疇。
調度器:最大程度保證多核CPU之間的平衡運行。
7-1V什么是線程調度,什么是線程切換
線程調度(也稱線程間的上下文切換),線程的執行總是趨向于CPU
受限或者I/O受限。也就是說線程的調度只分為兩類:一些線程需
要花費一定的時間使用CPU進行計算,另一些線程會花費一些時間
等待相對較慢的I/O操作完成。
調度器會依據它對線程的趨向性的猜測把他們進行分類,并讓I/O
受限的線程具有更高的動態優先級以及優先使用CPU。調度器會
認為I/O操作往往會花費更長的時間,所以應該讓它們盡早執行。
這也是為了讓眾多線程運行的更加高效。在人決定下一個要敲擊的
按鍵、磁盤在磁道中定位簇或者網卡從網絡中接收數據幀的時候,
CPU可以騰出手來位其它線程服務。
7-2v線程的靜態、動態優先級調用
線程的動態優先級是可以被調度器實時調整的,而與之相對應的線
程的靜態優先級只能由程序指定。如果應用程序沒有指定一個線程的
靜態優先級,那么默認為0。調度器不會改變線程的靜態優先級。
線程的動態優先級就是調度器在其靜態優先級的基礎上調整得出
的,動態優先級決定了線程的運行順序。
而線程的靜態優先級決定了線程單次在CPU上運行的最長時間,也
就是調度器分配給它的時間片的大小。
所有等待使用CPU的線程會按照動態優先級從高到低的進行順序
排序,并依序放到與該CPU對應的運行隊列當中。因此,下一個運
行的線程總是動態優先級最高的那一個。
7-3.線程的優先級隊列
每一個CPU的運行隊列中都包含兩個優先級陣列。其中一個用于
存放正在等待運行的線程,暫時稱之為(激活的優先級陣列)。另一
個則用于存放正在等待的運行的線程,暫時稱為(過期的優先級陣
歹U)。
下一個運行的線程總是會從激活的優先級隊列中選出。如果CPU
發現某個線程占用了CPU很久的時間,并且激活的優先級隊列中還
有優先級與他相同的線程在等待運行,那么調度器就會讓那個等待的
線程在CPU上運行,而被換下的則進入過期的優先級陣列。
當激活的優先級陣列中沒有等待運行的線程時,調度器會把這兩個
優先級陣列的身份互換,之前的激活陣列變成過期陣列,現在的過期
陣列變成激活陣列。如此,放入過期的優先級陣列的線程就又有機會
運行了。
當然,線程并不是總會處于運行和就緒狀態。他還有可能會進入阻
塞睡眠狀態,處于睡眠狀態的線程是不能夠被調度和運行的。它們會
從兩個陣列中移除。
七、線程模型
線程的實現模型分為三個,分別是:用戶級線程模型、內核級線程模
型、兩級線程模型。
它們之間最大的差異就在于線程與內核實體(內核調度實體KES)
之間的對應關系。內核調度實體就是被內核調度器所調用的實體對
象。也被稱為內核級線程,是操作系統得最小運行單元。
1.用戶級線程模型(M:l):
此模型下的線程是由用戶級別的線程庫全權管理的。線程庫并不是
內核的一部分,而只是存儲在進程的用戶空間之中,這些線程的存在
對于內核而言是無法感知的。
用戶級線程并不是內核的調度器的調度對象。對線程的各種管理和
協調完全是用戶及程序的自主行為,與內核無關。應用程序在對線程
進行創建、終止、切換或同步等操作的時候,并不需要讓CPU從用
戶態轉變成內核態。
2.內核級線程模型(1:1):
該模型下的線程是由內核負責管理的,它門是內核的一部分。
應用程序對線程的創建、終止和同步都必須通過內核提供的系統調
用來完成(pthread」)。進程中的每一個線程都會與一個KES(內核
調度實體)相對應。也就是說,內核可以分別為每一個線程進行調度。
由此,內核級線程模型也被稱為1:1的線程實現。一對一線程實現
消除了多對一線程實現的很多弊端。可以真正的實現線程的并發運
行。
3.兩級線程模型(M:N):
兩級線程模型的目標是取前兩種模型的精華,去其糟粕,也成為多對
多的(M:N)線程實現。
與其他模型相比,兩級線程模型提供了更多的靈活性。在此模型
下一個進程可以與多個KSE(內核調度實體)相關聯,這與內核級
線程模型相似,不同點在于進程中的線程并不與KES——對應,
這些應用程序線程可以映射到同一個已經關聯的KES上。
實現了兩級線程模型的線程庫,會通過操作系統內核創建多個內
核級線程。然后它會通過這些內核級線程對應用程序線程進行調度。
大多數此類線程庫都可以將這些應用程序線程動態地與內核級線程
關聯。這樣的設計顯然使線程的管理工作更加復雜,因為這需要線程
庫與內核級線程共同努力和協作才能正確、有效的運行。
八、線程同步
線程同步的目的就是為了更好地協同工作或者維持數據的一致性。
1.共享數據的一致性
一個進程所擁有的相當一部分虛擬內存地址都可以被進程中所有
線程共享,所以這些共享數據大多是以內存的空間作為載體。如果兩
個線程同時讀取同一塊共享內存但獲取到的數據卻不相同,那么程序
很有可能就會出現某種錯誤。
這是因為,共享數據的一致性往往代表著某種約定,而只有在該約
定成立的前提下,多線程程序中的各個線程才能夠使相應的流程執行
正確。換句話說,婦果操作的共享數據的結果,總是與約定的結果相
同,就說明共享數據的一致性得到了保證。
實際上,保證共享數據一致性的最簡單最徹底的方法就是使該數據
成為一個不變量。例如:常量就是不變量,它不可能被改變,也就不
可能出現不一致的情況。因此無論當前程序中有多少個可能訪問常量
的線程,都不需要采取任何措施。但是程序中不可能都是常量,我們
需要通過額外的手段保證被多個線程共享的變量的一致性,這樣就有
了臨界區的概念。
2.臨界區
臨界區是只能被串行化訪問或執行的某個資源或者代碼,也被稱為
串行區域。保證臨界區有效的最佳方式就是利用同步機制。在針對多
線程程序的同步機制中包含了很多同步方法,包括原子性操作和互斥
量,以及條件變量。
3.互斥量
在同一時刻,只允許一個線程處于臨界區之內的約束,稱之為互斥
(mutex)o
每個線程在進入臨界區之前,都必須先鎖定某個對象,只有成功
鎖定對象的線程才會允許進入臨界區,否則就會阻塞,這種對象被稱
為互斥對象或互斥量。
互斥量有兩種狀態,既鎖定狀態、未鎖定狀態。互斥量每次只能
鎖定一次,處于已鎖定狀態的互斥量不能被再次鎖定。除非它已經解
鎖,否則任何線程都不能對它進行二次加鎖。如果對一個已鎖定的互
斥量進行加鎖操作,那么這個操作必定會失敗。成功鎖定互斥量的線
程會成為該互斥量的所有者,只有互斥量的所有者才能對其進行解
鎖。從這個角度講,多個線程對同一個互斥量的爭相鎖定也可以看作
是對互斥量的釋放。
當線程離開臨界區的時候,必須要對相應的互斥量進行解鎖。這
樣其它想進入該臨界區而被阻塞的線程才會被喚醒,并且有機會再次
嘗試鎖定該互斥量。在這些線程中只有一個線程會成功鎖定該互斥
量。注意:對同一個互斥量的鎖定與解鎖應該成隊出現(defer)。
4,線程安全性,怎么實現線程安全性?
如果有一個代碼塊,它可以被多個線程并發執行,且總能夠產生預
期的結果,那么該代碼塊就是線程安全的。
如果代碼塊對共享數據進行了更新操作,那么這個代碼塊就是非線
程安全的。但是如果該代碼塊處于臨界區中,那么這個代碼塊就是線
程安全的。
但是,為了實現線程安全,把所有的代碼都置于臨界區中雖然可行,
但也是一種最低效的方法。可以仔細從函數體中查找出操作共享數據
的代碼并用互斥量將它們保護起來。還可以將這寫代碼從函數體中分
離出來,然后再將它們聚集在一起成為一個函數或者一個結構體。為
這個函數或結構體實現線程安全性。
GMP
一、早期的GMP調度器
1.當內核線程獲取g。的協程時,會經過調度器訪問go的全局協程
隊列。go的全局協程隊列是由鎖來進行保護的。當拿到隊列的鎖之
后,會執行隊列中的首個goroutine協程,其余的goroutine依次向
前挪動一位。
2.內核執行完goroutine協程任務之后,會釋放鎖,并把這個goroutine
還回全局協程隊列,只不過這時候會把goroutine放在隊尾。
早期調度器的弊端:
L創建、銷毀、調度G都需要每個內核線程M獲取鎖,形成了激烈
的鎖競爭。
2.內核M轉移goroutine,會造成延遲和額外的系統負載。
3.系統調用(CPU在多個M之間切換)導致頻繁的線程阻塞和取消阻
塞操作,增加了系統開銷。
二、GMP模型簡介
GMP是三種元素的縮寫。M與P是相互引用的關系,P與G是一對
多的調用關系。
G:goroutine協程,go中的代碼片段,協程程序。
P:processor處理器,一個P代表執行了一個G。代碼片段中所必需
的資源(上下文環境)
M:Machine,一個M代表一個內核線程,或者稱為工作線程。
簡單來說,一個協程(G)的執行需要P(處理器)和M(內核)的支
持,一個M在與P關聯之后,就形成了一個有效的協程(G)運行環
境(內核線程+上下文環境)。每個P都會包含一個可運行的協程(G)
隊列(runq)。該隊列中的協程(G)會被依次傳遞給本地的P關聯
M,并獲得運行。
2-1x詳解M
L什么情況下會建立一個M
一個M代表一個內核線程。大多數情況下,創建M都是由于沒有
足夠的M來關聯P,并運行其中可運行的Go在系統執行系統監控和
垃圾回收等任務的時候,也會創建M。M是一個結構體。
2.M與P和G是如何關聯的
M是一個結構體,這個結構體中包含了關聯P,G的字段。
typemstruct{
go*g
curg*g
ppuintptr〃當前與M關聯的P
nextppuintptr〃潛在關聯的P
spinningbool〃表示M是否正在尋找可以運行的G。在尋找
過程中M會處于自旋狀態。Go在運行時可以講一個M和一個G鎖
在一起,一旦鎖定,這個M就只能運行這個G。
lockedg*g〃表示與M鎖定的G
3.M是什么時候被創建的如何創建的?
在M被創建之后,GO系統會先對它進行一番初始化,其中包括對自
身所持有的棧空間以及信號處理方面的初始化。
M在創建之初,M會被加入全局的M列表中。這時它的起始函數
和預聯的P也會被設置。當運行時,系統會為這個M專門創建一個
新的內核線程并與之關聯。如果M就做好了執行G的準備。
起始函數只有當M執行系統監控和垃圾回收任務的時候才會被設
置。全局M隊列在運行時系統需要的時候,會通過全局M隊列,獲
取到所有M的信息,同時防止M被當成垃圾回收掉。
4.空閑M,M停止會如何?
運行中的M有時也會被停止,比如在執行垃圾回收任務的過程中。
運行時系統在停止M的時候,會把它放入調度器的空閑M列表。這
很重要,因為在需要一個未被使用的M時,調度器會先嘗試從空間
的M中獲取。M是否空閑,僅以它是否存在于調度器的空閑M隊列
表中為依據。
5.能否設置M的最大數量?
單個Go程序所使用的M的最大數量是可以設置的,Go程序運
行的時候會先啟動一個引導程序,這個引導程序會為其建立必要的環
境。在初始化調度器的時候,它會對M的最大數量進行初始設置,
這個初始值是10000,也就是說一個Go程序最多可以使用10000個Mo
22詳解P
1.什么是P
P是能夠在M口運行的關鍵。GO的調度器會適時地讓P與不同
的M建立或者斷開關聯,以使P中那些匯運行的G能夠及時獲得運
行時機,這與操作系統內核在CPU之上實時的切換不同進程或線程
的情形類似。
P的最大數量實際上是對程序中并發運行的G的規模的一種限制。
P的數量即為可運行G的隊列的數量。一個G在被啟用,會先追加到
某個P的可運行隊列中,以等待運行。
一個P只有與M關聯在一起,才會使其可以運行G。
2.P的數量,如何改變P的數量
在g。程序啟動之初,引導程序會在初始化調度器時,對P的最大
數量進行設置。這里的默認值會與當前CPU的總核心數相同。一旦
發現環境變量COMAXPROCS的值大于0,引導程序就會認為我們想
要對P的最大數量進行設置。
它會先檢查一下比值的有效性;如果不大于預設的硬性上限值(256),
就會認為是有效的,否則就會被這個硬性上限值取代。
第一種方法:調用函數PROCS把想要設定的數量作為參數傳入。
第二種方法:在G。程序運行前設置GOMAXPROCS的環境變量值。
3.P脫離運行狀態
雖然Go并未對何時調用PROCS函數作限制,但是該函數調用的執
行會暫時讓所有的P都脫離運行狀態,并試圖阻止任何用戶級別G
的運行。
只有在新的P最大數量設定完成之后,調度器才會陸續恢復它們。
這對于程序的性能是非常大的消耗。所以,P的數量最好在init()函
數里設置。實際上多數情況下不改變也沒什么。
4.P的空閑列表
與空閑的M列表類型,G。中也存在一個空閑的P列表。當一個P
不再與任何M關聯的時候,調度器就會把它放入該列表;而當調度
器需要一個空閑的P關聯某個M時,就會從此列表中取出一。
進入空閑P列表的前提是,該P中可運行的G必須為空。
5.P的運行狀態
Pidle:此狀態表明當前P未與任何M存在關聯。
Prunnig:此狀態表明當前P正在與某個M關聯。
Psyscall:此狀態表名當前P中的運行的G正在進行系統調用。
Pgcstop:此狀態表明調度器需要停止調度。開始垃圾回收。
pdead:此狀態表明當前P已經不會再被使用。如果Go程序運行的
過程中,通過調用PROCS函數減少了P的最大數量,那么多余的P
就會被運行時系統置于此狀態。
P在創建之初是pgcstop,但是這并不意味著要垃圾回收。當P進行
初始化之后會進入pidleo
6.P的自由調度列表,如何提高G的復用率
每個P除了有一個可運行的G隊列外,還有一個自由G列表。這個
列表中包含了一些已經運行完成的Go
當go語句要啟用一個G的時候,調度器會先試圖從相應P的自由
G列表中獲取一個現成的G,來封裝這個go語句攜帶的函數。
如果自由G列表中的G太少,調度器會從可運行G列表中轉移一
部分到自由列表中。如此,只有自由G列表中的G也沒了的時候,
調度器才會創建新的G,最大可能的提高G的復用率。
23詳解G
1.什么是G,goroutine的調度原理
一個G就是一個goroutine,編程人員使用go,gofunc向系統中提
交并發任務。
g。的編譯器會把提交的g。語句變成內部函數newproc的調用,并
把g。函數以及參數都作為參數傳遞給這個函數。
調度器在接收到newproc這樣一個調用后,會先檢查go函數的合法
性,然后試圖從本地的自由G列表與運行G列表中獲取一個可用的
Go如果沒有,則新建一個G。
與M和P相同,G也有一個全局列表c新建的G會第一時間加入
全局G列表。這個列表記錄著全局中所有的G指針。
在初始化完成后,這個G會被存儲到本地P的runnext字段中,該
字段用于存放新的G,處于這個字段的G會被更早地運行。如果這時
的runnext字段已經有了一個G,那么這個G就會被“踢到”該P的
可運行G隊列的末尾。如果P的可運行隊列滿了,那么就只能追加到
調度器的可運行G隊列中等待運行。
2.G的運行狀態
Gidle:表示G剛被分配,還沒有初始化
Grunnable:表示當前G正在可運行隊列中等待運行。
Grunning:表示當前G正在運行。
GsysCall:表示當前G正在執行某個系統調用。
Gwaiting:表示當前G正在阻塞。
Gdead:表示當前G正在閑置。
Gcopystack:表示當前G的棧區正在移動。移動的原因可能是棧的擴
展或者收縮。
在運行時調度器用一個G封裝goroutine函數時,會先對這個G進行
初始化,一旦G準備就緒,其狀態就會被設置成Grunnableo一個G
真正被使用一定是在Grunnable之后。
三、GMP調度模型的策略
Go語言中的調度,是用來調度操作系統內核之外的程序。調度的對
象是MPG實例。
1.調度器的基本結構
調度器有自己的數據結構,形成此結構的主要目的就是更加方便的
管理和調度各個核心元素的實例,空閑M列表,空間P列表,可運
行G隊列和自由G隊列。
還有一些其它的重要字段:
1.重要字段
gcwaitinguint32表示是否需要因一些任務而停止調度。
stopwaitint32表示需要停止但仍未停止的P的數量
stopnodenote用于實現與stopwait相關的事件通知機制
sysmonwaituint32表示在停止調度期間系統監控任務是否在等待
sysmonnotenote用于實現與sysmonwait相關的事件通知機制
在g。調度器執行工作中,一些任務在執行前是需要暫停調度的,例
如垃圾回收任務中的某些子任務,又比如發起運行時恐慌的panic任
務。
2.gcwaiting>stopwait、stopnote都是串行運行時任務執行前后的輔
助協調手段。
Gcwaiting^topwait,stopnote:
該字段的值用于表示是否需要停止調度,在停止調度時,該值被設
置為1;在恢復調度時,該值被設定為0。
當一些調度任務在執行時只要發現gcwaiting的值為1,就會把當
前P的狀態設置為pgstop,然后逐漸遞減stopwait字段的值,當
stopwait字段的值遞減為0時,就說明所有的P的狀態都是pgstop,
這時就可以喚醒停止調度的任務了。
3.字段sysmonwait和sysmonnote針對的是系統監測任務。
sysmonwait>sysmonnote
在停止調度任務執行之前,系統監測任務也需要暫停。sysmonwait
就是表示是否已經暫停,0表示未暫停,1表示已經暫停。系統監測
任務是持續執行的,處于無限的循環當中。
每次調度器執行任務之初,系統監測程序都會先檢查調度情況,
一旦發現調度停止gcwaiting字段不=0,或者P都已經處于閑置狀態。
就會把sysmonwait的字段設置=1,并利用sysmonnote停止調度器自
身。
在恢復時會將sysmonwait設置=0,sysmonnote用來恢復系統監測
任務。
2.調度策略
全力查找:
調度器會為P來尋找可運行的G,如果沒有找到就會進入全力查找
的狀態,從各處搜索可以運行的G。如果當前P中實在找不到,就會
通過workstealing從別的P偷一個。
全力搜索的范圍(了解即可)
1.獲取已經終結準備回收的G
2.從本地P的可運行G列表中獲取
3.從全局所有P的可運行G列表中獲取
4.從調度器的可運行G列表中獲取
5.從網絡l/Onetpoller獲取G
如果經歷這些步驟還是沒有獲取到G,就會停止掉當前的M。當之后
的某一個時刻出現G之后,M才會被喚醒。
偷取機制:
當當前M關聯的P中沒有可運行的G時,會嘗試從其它M關聯的
P中偷取一個G放到當前M下運行,而不是銷毀線程。
分離機制:
當當前M線程因為G進行系統調用發生阻塞時,該M線程會釋放
綁定的P,并把整個關聯的P轉移給其它空閑的M線程進行關聯運
行。
并行限制:
設置GOMAXPROCSP的數量,最多有GOMAXPROCS個線程分布在多
個CPU上同時運行,
3.搶占
搶占也叫系統監測,由sysmon函數實現。主要監測以下任務:
L在需要時搶奪符合條件的P和G,搶占模式
2.在需要時進行強制GC,清掃堆
3.在需要時打印調度器的跟蹤信息
搶占P和G的途徑有兩個,首先是通過網絡I/O輪詢其獲取可運
行的Go其次是從調度器那里搶奪符合條件的P和Go
搶占P過程:
在搶占P的流程中,全局P列表中的所有P都會被檢查。程序會
先查看P的狀態,如果為系統調用和運行狀態,程序就會對P進行下
一步檢查,
調度器會檢查它的調度計數是否同步。P的調度計數由它的
schedick字段存儲。只要它的可運行G隊列中的某個G被取出運行了,
該字段值就會遞增。
同樣的在調度器中也會持有一個調度計數的備份,判斷這個備份
與P中的schedick字段值是否相同。如果不同,就會忽略對這個P的
進行一步檢查。
如果相同,就判斷距離上次同步該P的調度時間是否滿足10ms,
如果超過,救說明該P的G已經運行了太久,需要停止并把運行機會
讓給其它的G。
總結:
1.首先從網絡I/O中搶到要執行的Go
2.然后調度器會監測每一個P中的schedick字段,然后與調度器的
備份數據比較,如果相同。就會繼續判斷距離上一次同步該P的時
間是否超過10ms,如果超時就會搶占一個新的P來運行。
四、gofunc()都經歷了哪些過程
1.創建一個gofunc()后,會把go語句編程對內部函數newproc的調
用,并把g。函數以及其參數都作為參數傳遞給這個newproc函數。
2.當系統接收到這樣一個函數調用后,會檢查go函數以及其參數的
合法性。與M和P相同,運行時系統也會持有一個G的全局列表,
新建的G會在第一時間被加入該列表。這個全局列表的作用就是集中
存放當前運行時系統中的所有G指針。
3?然后初始化go函數變成一個G,設置該G的狀態和IDo當初始化
結束后就會將G存儲到本地的P中等待運行。
4.如果當前沒有跟M建立管理的P,則會放入全局隊列中等待出現與
M相關聯的P出現。
5.當M執行某一個G時發生了阻塞操作,M會阻塞,如果當前有一
些G正在運行,就會把這個M中的P剔除。然后再創建一個新的M,
或者全局M列表中拿到一個空閑的M來服務這個Po
五、調度器的生命周期,mO,gO
MO
L啟動進程后的執行的第一個線程,也是編號為0的主線程。
2.在全局變量runtime.mO中,不需要在heap上分配。
3.負責執行初始化操作和啟動第一個G的
4.當啟動第一個G之后,M0就和其它M一樣了。
GO
每次啟動一個M,都會立刻創建第一個goroutine,就是gO
gO僅用于負責調度其它G
gO本身不指向任何可指向的函數。
每個M都會有一個自己的gOo
在調度或系統調用時,會使用M切換到gO來進行調度。
M0的gO放在全局空間
六、Netpoller
網絡I/O輪詢器,在操作系統提供的異步I/O基礎組件之上,實現Go
自己的阻塞式I/O而編寫的子程序。
當一個G試圖在一個網絡連接上進行讀寫操作的時候,底層程序
就會開始為此準備,這時這個G就會進入阻塞狀態。
一旦G準備就緒,就會讓netpoller立即通知為此等待的G,返
回給它相應的事件。
因此,從netpoller處獲取G的意思,就是獲取那些已經接收到
通知的G,此時這個G就可以進行網絡讀寫操作了,調度器會讓它進
入Grunnable準備狀態,并等待運行。
七、M自旋
在GMP中,M自旋是代表M的一種工作狀態。M處于自旋狀態,
意味著M還沒有找到要執行的Go這種狀態需要循環持續監聽是否
有G出現在全局隊列。
如果M停止,就會退出自旋狀態。一般情況下,調度器中至少要
保證一個M處于自旋。
如果發現調度器中沒有自旋M了,就會創建一個新的M來啟動Go
當創建或者恢復啟動M時,M會自動進入自旋狀態。
Coroutine
一、goroutine什么時候會發生阻塞?
場景1:由于原子、互斥量或通道操作調用導致Goroutine阻塞,調
度器將把當前阻塞的Goroutine
場景2:由于網絡請求和10操作導致Goroutine阻塞,這種阻塞的
情況下,我們的G和M又會怎么做呢?
Go程序提供門網絡輪詢器(NetPoller)來處理網絡請求和10操作
的問題,其后臺通過kqueue(MacOS),epolKLinux)?£iocp(Windows)
來實現10多路復用。
場景3:當調用一些系統方法的時候,如果系統方法調用的時候發生
阻塞,這種情況下,網絡輪詢器(NetPoller)無法使用,而進行系統
調用的Goroutine將阻塞當前M。
場景4:如果在Goroutine去執行一個sleep操作,導致M被阻塞
了。
場景5:channel只寫沒有接收的時候會出現阻塞,代碼層阻塞。
系統調用阻塞
當M因為系統調用而阻塞,G進入系統調用的時候,調度器會把
該M和P分離開。這時,如果這個P的可運行G隊列中還有未被運
行的G,那么調度器就會找到一個空閑的M,或創建一個新的M,并
與該P關聯,以滿足這些G的運行需要。因此M的數量一般都要比
P多。
而那個阻塞的G系統調用,會與運行它的M鎖住,直到阻塞結束,
才會釋放鎖。這時將M重新歸入空閑的M隊列,等待下一次的調用。
執行等待操作進入阻塞
如果goroutine的go代碼中包含對channel通道值得等待操作,那
么在執行到對應代碼得時候,這個G就會進入阻塞狀態。需要等待從
通道類型值中接收數據。
此外,操作定時器,sleep函數也會造成G得等待。在事件到來之
前一直會處于阻塞。直到事件到來之后,G才會被喚醒,并轉換至等
待運行得狀態。
二、goroutine有幾種狀態?自旋狀態是什么?
goroutine的狀態就是G的狀態,分為:初始化、等待運行、正在
運行、系統調用、阻塞、閑置、棧移動(回收)狀態。
goroutine的自旋是M內核線程的自旋。在M的結構體中有一個
spinning的字段,該字段用來表示M是否正在尋找可運行的
G(goroutine)o在尋找過程
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 工藝保護行業直播電商戰略研究報告
- 高品質鋁鍛件行業跨境出海戰略研究報告
- 小品表演行業跨境出海戰略研究報告
- 水泥線條企業制定與實施新質生產力戰略研究報告
- 高碳錳鐵行業直播電商戰略研究報告
- 食品安全檢測儀器行業直播電商戰略研究報告
- 2025年精制碘鹽項目可行性研究報告
- 2025年筆式電子體溫計項目可行性研究報告
- 25年公司、項目部、各個班組安全培訓考試試題全套
- 2024-2025項目管理人員年度安全培訓考試試題B卷
- 工業互聯網+危險化學品企業 安全風險智能化管控平臺
- 《居家養老服務規范》
- 2025年福建能化集團招聘筆試參考題庫含答案解析
- 邁瑞除顫儀培訓
- 2024-2030年中國高壓變頻器行業現狀分析及前景趨勢調研報告
- 2024年度中國船員心理健康報告
- 《地源熱泵介紹》課件
- 5以內數的守恒-課件
- 2024年第四屆全國工業設計職業技能大賽決賽包裝設計師理論考試題庫(含答案)
- 幼兒園小班認識小動物課件
- GB/T 44569.1-2024土工合成材料內部節點強度的測定第1部分:土工格室
評論
0/150
提交評論