日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

弄懂goroutine调度原理

發(fā)布時間:2024/7/5 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 弄懂goroutine调度原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

goroutine簡介

golang語言作者Rob Pike說,“Goroutine是一個與其他goroutines 并發(fā)運行在同一地址空間的Go函數(shù)或方法。一個運行的程序由一個或更多個goroutine組成。它與線程、協(xié)程、進(jìn)程等不同。它是一個goroutine“。

  • goroutine通過通道來通信,而協(xié)程通過讓出和恢復(fù)操作來通信;
  • goroutine 通過Golang 的調(diào)度器進(jìn)行調(diào)度,而協(xié)程通過程序本身調(diào)度;

簡單的說就是Golang自己實現(xiàn)了協(xié)程并叫做goruntine(本文稱Go協(xié)程),且比協(xié)程更強大。

goroutine調(diào)度原理

上面說到Go協(xié)程是通過Golang的調(diào)度器進(jìn)行調(diào)度的,其中調(diào)度器的線程模型為兩級線程模型。

有關(guān)兩級線程模型的介紹,可以看這篇文章

我們來看下Golang實現(xiàn)的兩級線程模型是怎樣的。首先要知道這三個字母代表的含義

  • M:代表內(nèi)核級的線程
  • P:全程Processor,代表運行Go協(xié)程所需要的資源(上下文環(huán)境)
  • G:代表Go協(xié)程

    我們先看下為實現(xiàn)調(diào)度Golang定義了這些數(shù)據(jù)結(jié)構(gòu)存M,P,G
名稱作用范圍描述
全局M列表Go的運行時存放所有M的單向鏈表
全局P列表Go的運行時存放所有P的數(shù)組
全局G列表Go的運行時存放所有G的切片
調(diào)度器的空閑M列表調(diào)度器存放空閑M的單向鏈表
調(diào)度器的空閑P列表調(diào)度器存放空閑P的單向鏈表
調(diào)度器的自由G列表調(diào)度器存放自由G的單向鏈表(有兩個)
調(diào)度器的可運行G隊列調(diào)度器存放可運行G的隊列
P的自由G列表本地P存放當(dāng)前P中自由G的單向鏈表
P的可運行G隊列本地P存放當(dāng)前P中可運行G的隊列

然后從上往下解析Go的兩級線程模型圖

(1)M和內(nèi)核線程之間是一對一的關(guān)系,一個M在其生命周期中,只會和一個內(nèi)核線程關(guān)聯(lián),所以不會出現(xiàn)對內(nèi)核線程的頻繁切換;

Golang的運行時執(zhí)行系統(tǒng)監(jiān)控和垃圾回收等任務(wù)時候會導(dǎo)致創(chuàng)建M,M空閑時不會被銷毀,而是放到一個調(diào)度器的空閑M列表中,等待與P關(guān)聯(lián),M默認(rèn)數(shù)量為10000

(2)P和M之間是多對多的關(guān)系,P和G之間是一對多的關(guān)系,他們的關(guān)聯(lián)是易變的,由Golang的調(diào)度器完成調(diào)度;

Golang的運行時按規(guī)則調(diào)度,讓P和不同的M建立或斷開關(guān)聯(lián),使得P中的G能夠及時獲得運行時機(jī)

(3)P的數(shù)量默認(rèn)為CPU總核心數(shù),最大為256,當(dāng)P沒有可運行的G時候(P的可運行G隊列為空),P會被放到調(diào)度器的空閑P列表中,等待M與它關(guān)聯(lián);

P有可能會被銷毀,如運行時用runtime.GOMAXPROCS把P的數(shù)量從32降到16時,剩余16個會被銷毀,它們原來的G會先轉(zhuǎn)到調(diào)度器可運行的G隊列和自由G列表

(4)每個P中有可運行的G隊列(如圖中最下面的那行G)和自由G列表(圖中未畫出來),當(dāng)G的代碼執(zhí)行完后,該G不會被銷毀,而是被放到P的自由G列表或調(diào)度器的自由G列表。如果程序新建了Go協(xié)程,調(diào)度器會在自由G列表中取一個G,然后把Go協(xié)程的函數(shù)賦值到G中(如果自由G列表為空,就創(chuàng)建一個G);

可見Golang調(diào)度器在調(diào)度時很大程度復(fù)用了M,P,G

(5)在Go程序初始化后,調(diào)度器首先進(jìn)行一輪調(diào)度,此時用M去搜索可運行的G。其中我們的main函數(shù)也是一個G,找到可運行的G后就執(zhí)行它;

至于怎么找可運行的G呢?答案是到處找,想盡辦法找(這里只列出一部分地方)。

  • 從本地P的可運行的G隊列找
  • 從調(diào)度器的可運行的G隊列找
  • 從其他P的可運行的G隊列找

(6)P的可運行G隊列最大只能存放長度為256的G,當(dāng)隊列滿后,調(diào)度器會把一半的G轉(zhuǎn)到調(diào)度器的可運行G隊列。

系統(tǒng)監(jiān)控

上面大概描述了關(guān)于goroutine調(diào)度的流程。現(xiàn)在還存在一個問題,那就是當(dāng)Go協(xié)程很多(并發(fā)量大)時候,顯然G是不能一直執(zhí)行下去的,因為也需要把執(zhí)行機(jī)會留給其他的G。此時Golang運行時的系統(tǒng)監(jiān)控就起作用了。
一般情況,當(dāng)G運行時間超過10ms后,該G就會被系統(tǒng)告知需要停止了,讓其他G運行。(這里情況比較復(fù)雜,并不能確保每個G都能被公平執(zhí)行)

以下特殊情況該G不需要停止

  • P的可運行G隊列為空(沒有其他G可運行)
  • 有空閑的M在尋找可運行的G(沒有其他G可運行)
  • 空閑的P(還有P閑著)

總結(jié)

Golang以兩級線程實現(xiàn)模型,自己實現(xiàn)goruntine和調(diào)度器,優(yōu)勢在于并行和非常低的資源使用。

主要體現(xiàn):

  • 內(nèi)存消耗方面(每個Go協(xié)程占的內(nèi)存遠(yuǎn)小于線程占的內(nèi)存)
  • 切換(調(diào)度)開銷方面
  • 線程切換涉及模式切換(從用戶態(tài)切換到內(nèi)核態(tài))

此外,Go協(xié)程執(zhí)行任務(wù)完成的順序并不都是按我們預(yù)期的那樣(程序不加以控制的情況下),特別在一些耗時較長的任務(wù)中。且每個Go協(xié)程執(zhí)行的時間也不是絕對公平的。

如有錯誤地方,還請狂噴!

轉(zhuǎn)載于:https://www.cnblogs.com/FireworksEasyCool/p/11508806.html

總結(jié)

以上是生活随笔為你收集整理的弄懂goroutine调度原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。