欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang并發(fā)編程深入分析

 更新時間:2022年11月18日 14:24:59   作者:Mingvvv  
golang中的并發(fā),是函數(shù)相互獨立運行的能力,goroutines是并發(fā)運行的函數(shù)。golang提供了goroutines作為并發(fā)處理的一種方式

Go 協(xié)程和普通線程對比

Go 擁有極強的并發(fā)編程能力,而 Go 并發(fā)編程強勢原因,一部分原因是因為語法簡單 ,還有一個更核心的原因是 Go 中協(xié)程 goroutine (用戶態(tài)線程)的存在。

在 Go 中 goroutine 是比普通線程(內(nèi)核態(tài)線程)更加輕量化的存在。

內(nèi)核級線程(線程)

內(nèi)核態(tài)線程簡稱線程,是在內(nèi)核中維護(hù)了線程表進(jìn)行跟蹤監(jiān)控的線程。是 CPU 調(diào)度和分派的基本單位,是具備進(jìn)程某些屬性,能夠獨立運行的更小單位,所以也稱為輕量級進(jìn)程。

如果使用內(nèi)核態(tài)線程在一個 CPU 上實現(xiàn)任務(wù)的并發(fā),那么 CPU 會通過分配時間片的方式去執(zhí)行任務(wù),當(dāng)正在運行的任務(wù)所分配的時間片用完了,就會切換到另一個任務(wù),而在切換的之前會保存當(dāng)前任務(wù)的狀態(tài)(CPU寄存器、程序計數(shù)器中的內(nèi)容),當(dāng)下次再次切換到這個任務(wù)之前就會先加載之前保存到任務(wù)狀態(tài),這個過程稱作上下文切換。

而在上下文切換的過程該線程會從用戶態(tài)轉(zhuǎn)為內(nèi)核態(tài),加載時再由內(nèi)核態(tài)轉(zhuǎn)為用戶態(tài)。而用戶態(tài)和內(nèi)核態(tài)切換的代價是很高的,從用戶態(tài)到內(nèi)核態(tài)的切換比較耗費資源的,而且內(nèi)核資源是很珍貴的,所以內(nèi)核態(tài)線程過多時,可能會出現(xiàn)內(nèi)核資源耗盡的情況。

線程優(yōu)點

  • 多核 CPU 利用 :內(nèi)核具有0級權(quán)限,因此可以在多個 CPU 上執(zhí)行內(nèi)核線程
  • 操作系統(tǒng)級優(yōu)化:線程之間是相對獨立的,一個線程阻塞不會影響其他線程的執(zhí)行。內(nèi)核態(tài)線程在進(jìn)行你 IO 操作時不需要進(jìn)行系統(tǒng)調(diào)用 。

線程缺點

  • 創(chuàng)建的時候需要切換到內(nèi)核態(tài),所以創(chuàng)建成本比較高。
  • 切換的時候,需要用戶態(tài)和內(nèi)核態(tài)之間頻繁的切換,切換成本較高。
  • 線程數(shù)量有限制,當(dāng)內(nèi)核態(tài)線程過多時,會出現(xiàn)內(nèi)核資源耗盡的情況。

用戶級線程(協(xié)程)

用戶態(tài)線程簡稱協(xié)程,是完全由用戶控制的線程,內(nèi)核并不會感知到它的存在,用戶級線程的創(chuàng)建、銷毀、調(diào)度、狀態(tài)變更以及其中的代碼和數(shù)據(jù)都完全需要我們的程序自己去實現(xiàn)和處理。因為是存在在用戶空間上的線程,所以在切換時不存在用戶態(tài)和內(nèi)核態(tài)的轉(zhuǎn)換。

協(xié)程優(yōu)點

  • 不由內(nèi)核管理,由用戶自主管理,創(chuàng)建成本比較低。
  • 不存在內(nèi)核態(tài)和用戶態(tài)的切換,切換成本比較低。
  • 不需要操作系統(tǒng)去調(diào)度,所以可以跨操作系統(tǒng),并且更加靈活、更容易控制。

協(xié)程缺點

  • 因為操作系統(tǒng)的內(nèi)核看不到協(xié)程,所以同屬于一個進(jìn)程的協(xié)程只能占有一個核,不能發(fā)揮多核優(yōu)勢。
  • 操作系統(tǒng)不能主動調(diào)度協(xié)程,所以后面的協(xié)程只能等待前面的寫成執(zhí)行完才能獲取到 CPU 資源。
  • 同上,當(dāng)前協(xié)程阻塞會導(dǎo)致后面的寫成無法執(zhí)行。內(nèi)核協(xié)作成本高,當(dāng)要進(jìn)行一些高權(quán)限的操作,比如讀寫文件時,需要頻繁地進(jìn)行用戶態(tài)和內(nèi)核態(tài)的切換。

調(diào)度器(GPM)

GPM 是 Go 語言運行時系統(tǒng)的重要組成部分

其中 G 代表 goroutine 即協(xié)程,M 代表 machine 即系統(tǒng)線程、P 代表 Proccessor 代表寫成和線程之間的聯(lián)系

簡單點理解就是:

G 是要被執(zhí)行的任務(wù)實例

M 是實際的執(zhí)行載體,需要綁定 P 成為一個執(zhí)行單元才能調(diào)度 G

P 可以看作是任務(wù)處理器,P 中保存 M 執(zhí)行 G 時的一些資源,P 決定了哪個 G 能配分配到哪個 M 上

G\P\M 之間的工作關(guān)系

G 要調(diào)度到 M 上才能運行,M 需要關(guān)聯(lián) P 才可以執(zhí)行 Go 代碼,但當(dāng)處理阻塞或系統(tǒng)調(diào)用中時,M 可以不用關(guān)聯(lián) P

當(dāng)進(jìn)程啟動時,會創(chuàng)建若干個(一般是內(nèi)核數(shù)量)系統(tǒng)線程 M 和任務(wù)處理器 P,并一一綁定,因為一個線程對應(yīng)一個 CPU 就不會出現(xiàn)上下文的切切換,能更大限度的節(jié)省資源。同時每一個任務(wù)處理器 P 都會對應(yīng)一個私有的任務(wù)隊列,但是私有隊列有上限的,所以所有任務(wù)處理器還會有一個公用的全局隊列。

當(dāng)有一個 G 被創(chuàng)建時,就會被放到一個任務(wù)處理器 P 的私有隊列中等待被調(diào)度。如果所有的私有隊列都滿了,就會被放到全局隊列中去。當(dāng) P 尋找 G 的時候,會先從自己的私有隊列中尋找,如果沒找到,再去全局隊列中尋找,如果還是沒有,它會去搶別的 P 中的 G。當(dāng)任何地方都不能找到需要調(diào)度的 G 時,M 和 P 則會斷開連接。

當(dāng) G 中進(jìn)行了系統(tǒng)調(diào)用,則 M 也會進(jìn)入系統(tǒng)調(diào)用狀態(tài),此時 P 會去尋找其他未在工作的 M 并為其尋找需要調(diào)度的任務(wù)。當(dāng) G 完成了系統(tǒng)調(diào)用,會進(jìn)入空閑的私有隊列或者全局隊列,然后等待再次被調(diào)度。

所以,G、M 之間沒有直接的聯(lián)系,一個 G 可能被不同的 M 調(diào)度,一個 M 也可以調(diào)度不同的 G。

Go 使用協(xié)程

創(chuàng)建協(xié)程

通過關(guān)鍵字 go 創(chuàng)建一個協(xié)程

一:普通函數(shù)創(chuàng)建

func goroutineTest(i int) {
	fmt.Println(i)
}
func main() {
	for i := 0; i < 10; i++ {
		go goroutineTest(i)
	}
	time.Sleep(time.Millisecond * 500)
}

二:匿名函數(shù)創(chuàng)建

func main() {
	for i := 0; i < 10; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
	time.Sleep(time.Millisecond * 500)
}

三:匿名帶參數(shù)函數(shù)創(chuàng)建

func main() {
	for i := 0; i < 10; i++ {
		go func(str string) {
			fmt.Println(str)
		}(fmt.Sprintf("%s%d", "s", i))
	}
	time.Sleep(time.Millisecond * 500)
}

注意

一:主協(xié)程不會等待子協(xié)程執(zhí)行完畢

以下列代碼為例,一般來說控制臺不會打印任何東西。原因是當(dāng)代碼運行到 go goroutineTest(i) 這一行時,并不是真正的會運行它,而是將它放到了任務(wù)隊列中。所以可能子協(xié)程還沒開始執(zhí)行,主協(xié)程就已經(jīng)退出了。所以我們可以再主協(xié)程加上等待。

func goroutineTest(i int) {
	fmt.Println(i)
}
func main() {
	for i := 0; i < 10; i++ {
		go goroutineTest(i)
	}
	time.Sleep(time.Millisecond * 500)
}

二:協(xié)程的執(zhí)行沒有順序

同上,當(dāng)寫成創(chuàng)建是,并不是立即執(zhí)行,而是被分到不同的隊列中等待被調(diào)度,而調(diào)度是不能保證先后順序的(只能保證同一個隊列中的任務(wù)先進(jìn)先出),因此上述代碼打印出來的結(jié)果大部分情況下時亂序的。

三:無參匿名函數(shù)中的變量變化

func main() {
	for i := 0; i < 3; i++ {
		go func() {
			 fmt.Println(i)
		}()
	}
	time.Sleep(time.Millisecond * 500)
}

這段代碼的打印結(jié)果如下:

2
2
3

因為匿名函數(shù)不攜帶參數(shù)信息,只有匿名方法執(zhí)行到 fmt.Println(i) 這一行時才會去讀取 i 的值。又因為協(xié)程執(zhí)行是有延遲的,所以當(dāng)執(zhí)行到這一句時, i 的值可能已經(jīng)發(fā)生了變化。

下一篇再研究主協(xié)程等待子協(xié)程執(zhí)行完畢以及確保子協(xié)程執(zhí)行順序的方法…

到此這篇關(guān)于Golang并發(fā)編程深入分析的文章就介紹到這了,更多相關(guān)Golang并發(fā)編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go語言的變量定義示例詳解

    go語言的變量定義示例詳解

    這篇文章主要為大家介紹了go語言的變量定義示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • golang下的viper包的簡單使用方式

    golang下的viper包的簡單使用方式

    這篇文章主要介紹了golang下的viper包的簡單使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Go類型安全的HTTP請求示例詳解

    Go類型安全的HTTP請求示例詳解

    這篇文章主要為大家介紹了Go類型安全的HTTP請求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Golang學(xué)習(xí)筆記(二):類型、變量、常量

    Golang學(xué)習(xí)筆記(二):類型、變量、常量

    這篇文章主要介紹了Golang學(xué)習(xí)筆記(二):類型、變量、常量,本文講解了基本類型、保留字、變量、常量、枚舉、運算符、指針、分組聲明等內(nèi)容,需要的朋友可以參考下
    2015-05-05
  • Go語言中內(nèi)存管理逃逸分析詳解

    Go語言中內(nèi)存管理逃逸分析詳解

    所謂的逃逸分析(Escape?analysis)是指由編譯器決定內(nèi)存分配的位置嗎不需要程序員指定。本文就來和大家簡單分析一下Go語言中內(nèi)存管理逃逸吧
    2023-03-03
  • 基于Go+OpenCV實現(xiàn)人臉識別功能的詳細(xì)示例

    基于Go+OpenCV實現(xiàn)人臉識別功能的詳細(xì)示例

    OpenCV是一個強大的計算機(jī)視覺庫,提供了豐富的圖像處理和計算機(jī)視覺算法,本文將向你介紹在Mac上安裝OpenCV的步驟,并演示如何使用Go的OpenCV綁定庫進(jìn)行人臉識別,需要的朋友可以參考下
    2023-07-07
  • Go語言為什么不支持三元運算符原理解析

    Go語言為什么不支持三元運算符原理解析

    這篇文章主要為大家介紹了Go語言為什么不支持三元運算符原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Golang實現(xiàn)心跳機(jī)制的示例詳解

    Golang實現(xiàn)心跳機(jī)制的示例詳解

    這篇文章主要為大家詳細(xì)介紹了Golang實現(xiàn)心跳機(jī)制的相關(guān)知識,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以參考一下
    2024-04-04
  • Go語言MD5加密用法實例

    Go語言MD5加密用法實例

    這篇文章主要介紹了Go語言MD5加密用法,實例分析了Go語言MD5加密的使用技巧,需要的朋友可以參考下
    2015-03-03
  • Goland使用delve進(jìn)行遠(yuǎn)程調(diào)試的詳細(xì)教程

    Goland使用delve進(jìn)行遠(yuǎn)程調(diào)試的詳細(xì)教程

    網(wǎng)上給出的使用delve進(jìn)行遠(yuǎn)程調(diào)試,都需要先在本地交叉編譯或者在遠(yuǎn)程主機(jī)上編譯出可運行的程序,然后再用delve在遠(yuǎn)程啟動程序,本教程會將上面的步驟簡化為只需要兩步,1,在遠(yuǎn)程運行程序2,在本地啟動調(diào)試,需要的朋友可以參考下
    2024-08-08

最新評論