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

Go?并發(fā)編程協(xié)程及調(diào)度機(jī)制詳情

 更新時(shí)間:2022年09月08日 11:13:27   作者:宇宙之一粟  
這篇文章主要介紹了Go并發(fā)編程協(xié)程及調(diào)度機(jī)制詳情,協(xié)程是Go語言最大的特色之一,goroutine的實(shí)現(xiàn)其實(shí)是通過協(xié)程,更多相關(guān)內(nèi)容需要的朋友可以參考一下

前言:

協(xié)程(coroutine)是 Go 語言最大的特色之一,goroutine 的實(shí)現(xiàn)其實(shí)是通過協(xié)程。

協(xié)程的概念

協(xié)程一詞最早出現(xiàn)在 1963 年發(fā)表的論文中,該論文的作者為美國(guó)計(jì)算機(jī)科學(xué)家 Melvin E.Conway。著名的康威定律:“設(shè)計(jì)系統(tǒng)的架構(gòu)受制于產(chǎn)生這些設(shè)計(jì)的組織的溝通結(jié)構(gòu)。” 也是這個(gè)作者。

協(xié)程是一種用戶態(tài)的輕量級(jí)線程,可以想成一個(gè)線程里面可以有多個(gè)協(xié)程,而協(xié)程的調(diào)度完全由用戶控制,協(xié)程也會(huì)有自己的 registers、context、stack 等等,并且由協(xié)程的調(diào)度器來控制說目前由哪個(gè)協(xié)程執(zhí)行,哪個(gè)協(xié)程要被 block 住。

而相對(duì)于 Thread 及 Process 的調(diào)度,則是由 CPU 內(nèi)核去進(jìn)行調(diào)度,因此操作系統(tǒng)其實(shí)會(huì)有所謂許多的調(diào)度算法,并且可以進(jìn)行搶占式調(diào)度,可以主動(dòng)搶奪執(zhí)行的控制權(quán)。

反之,協(xié)程是不行的,只能進(jìn)行非搶占式的調(diào)度。 可以理解成,如果 coroutine 被 block 住,則會(huì)在用戶態(tài)直接切換另外一個(gè) coroutine 給此 thread 繼續(xù)執(zhí)行,這樣其他 coroutine 就不會(huì)被 block 住,讓資源能夠有效的被利用,借此實(shí)現(xiàn) Concurrent 的概念。

協(xié)程與線程

線程 是 CPU 調(diào)度的基本單位,多個(gè)線程可以通過共享進(jìn)程的資源,通過共享內(nèi)存等方式來進(jìn)行線程間通信。

協(xié)程 可理解為輕量級(jí)線程,與線程相比,協(xié)程不受操作系統(tǒng)系統(tǒng)調(diào)度,協(xié)程調(diào)度由用戶應(yīng)用程序提供,協(xié)程調(diào)度器按照調(diào)度策略把協(xié)程調(diào)度到線程中運(yùn)行。

  • 協(xié)程只需花幾 KB 就可以被創(chuàng)立,線程則需要幾 MB 的內(nèi)存才能創(chuàng)立
  • 切換開銷方面,協(xié)程遠(yuǎn)遠(yuǎn)低于線程,切換的速度也因此大幅提升

goroutine 的誕生

Golang 語言的 goroutine 其實(shí)就是協(xié)程,特別的是在語言層面直接原生支持創(chuàng)立協(xié)程,并在 runtime、系統(tǒng)調(diào)用等多方面對(duì) goroutine 調(diào)度進(jìn)行封裝及處理。

相對(duì)于 Java 的建立線程,操作系統(tǒng)是會(huì)直接建立一個(gè)線程與其對(duì)應(yīng),而多個(gè)線程的間互相切換需要通過內(nèi)核線程來進(jìn)行,會(huì)有較大的上下文切換開銷,造成的資源耗費(fèi),而 goroutine 是在代碼上直接實(shí)現(xiàn)切換,不需要經(jīng)過內(nèi)核線程。

goroutine 的優(yōu)勢(shì):

  • 與線程相比, goroutine 非常便宜,可以根據(jù)應(yīng)用程序的需求自動(dòng)分配, 但在線程的大小通常是固定的
  • 使用 goroutine 訪問共享內(nèi)存的時(shí)候 透過 channel 可以避免競(jìng)態(tài)條件的發(fā)生

比如,我們計(jì)算一個(gè)數(shù)字的質(zhì)數(shù),可以寫出如下的代碼:

package main

import (
	"fmt"
	"time"
)

func main() {
	num := 300000
	start := time.Now()
	for i := 1; i <= num; i++ {
		if isPrime(i) {
			fmt.Println(i)
		}
	}
	end := time.Now()
	fmt.Println(end.Unix()-start.Unix(), "seconds")
}
func isPrime(num int) bool {
	if num == 1 {
		return false
	} else if num == 2 {
		return true
	} else {
		for i := 2; i < num; i++ {
			if num%i == 0 {
				return false
			}
		}
		return true
	}
}

上面的代碼用 num := 300000 來測(cè)試,也就是從 1~300000 之間來看那些數(shù)字會(huì)是質(zhì)數(shù),如果是質(zhì)數(shù)的話就把質(zhì)數(shù)輸出,最后看到最終花費(fèi)了 37 秒。運(yùn)行結(jié)果如下:

使用 goroutine 加快速度

package main

import (
	"fmt"
	"time"
)

func main() {
	num := 300000
	start := time.Now()
	for i := 1; i <= num; i++ {
		go findPrimes(i)
	}

	end := time.Now()
	time.Sleep(5 * time.Second)
	fmt.Println(end.Unix()-start.Unix(), "seconds")
}

func findPrimes(num int) {
	if num == 1 {
		return
	} else if num == 2 {
		fmt.Println(num)
	} else {
		for i := 2; i < num; i++ {
			if num%i == 0 {
				return
			}
		}
		fmt.Println(num)
	}
}

go findPrimes 這條語句就可以開啟一個(gè) goroutine,因此以主程序來說這樣等于是開啟 300000 個(gè) goroutine 來各自判斷自己拿到 num 是不是質(zhì)數(shù)這樣。

用 time. Sleep 來休息五秒來讓 main 主程序不要被關(guān)閉,否則由于開啟 goroutine 之后代碼會(huì)繼續(xù)往下執(zhí)行,如果沒做 sleep 的話會(huì)導(dǎo)致主程序關(guān)閉,主程序一關(guān)閉 goroutine 就跟著關(guān)閉了,我們就看不出效果了。

這邊運(yùn)行之后會(huì)發(fā)現(xiàn)輸出的質(zhì)數(shù)出現(xiàn)并不是從小到大的,這是因?yàn)檫@些 goroutine 是一起做事情的,所以誰先做完誰就先輸出這樣。

運(yùn)行結(jié)果如下,最后花費(fèi)了大概 11 秒:

goroutine 的機(jī)制原理

理解 goroutine 機(jī)制的原理,關(guān)鍵是理解 Go 語言是如何實(shí)現(xiàn)調(diào)度器模型的。

計(jì)算機(jī)科學(xué)領(lǐng)域的任何問題都可以通過添加間接中間層來解決。GPM 模型就是這一理論的實(shí)踐者。

Go 語言中支撐整個(gè)調(diào)度器實(shí)現(xiàn)的主要有 4 個(gè)重要結(jié)構(gòu),分別是 machine(簡(jiǎn)稱 M )、goroutine(簡(jiǎn)稱 G )、processor(簡(jiǎn)稱 P )、Scheduler(簡(jiǎn)稱 Sched), 前三個(gè)定義在 runtime.h 中,Sched 定義在 proc.c 中。

  • Sched 結(jié)構(gòu)就是調(diào)度器,它維護(hù)有存儲(chǔ) M 和 G 的隊(duì)列以及調(diào)度器的一些狀態(tài)信息等
  • M 結(jié)構(gòu)是 Machine,系統(tǒng)線程,它由操作系統(tǒng)管理和調(diào)度的,goroutine 就是跑在 M 之上的; M 是一個(gè)很大的結(jié)構(gòu),里面維護(hù)小對(duì)象內(nèi)存 cache(mcache)、當(dāng)前執(zhí)行的 goroutine、隨機(jī)數(shù)發(fā)生器等等非常多的信息。
  • P 結(jié)構(gòu)是 Processor,處理器,它的主要用途就是用來執(zhí)行 goroutine 的,它維護(hù)了一個(gè) goroutine 隊(duì)列,即 runqueue。 Processor 是讓我們從 N:1 調(diào)度到 M:N 調(diào)度的重要部分。
  • G 是 goroutine 實(shí)現(xiàn)的核心結(jié)構(gòu),它包含了棧,指令指針,以及其他對(duì)調(diào)度 goroutine 很重要的信息,例如其阻塞的 channel。

我們分別用三角形,矩形和圓形表示 Machine Processor 和 Goroutine:

在單核處理器的場(chǎng)景下,所有 goroutine 運(yùn)行在同一個(gè) M 系統(tǒng)線程中,每一個(gè) M 系統(tǒng)線程維護(hù)一個(gè) Processor,任何時(shí)刻,一個(gè) Processor 中只有一個(gè) goroutine,其他 goroutine 在 runqueue 中等待。

一個(gè) goroutine 運(yùn)行完自己的時(shí)間片后,讓出上下文,回到 runqueue 中。 多核處理器的場(chǎng)景下,為了運(yùn)行 goroutines,每個(gè) M 系統(tǒng)線程會(huì)持有一個(gè) Processor 。

可以看到 Go 的并發(fā)用起來非常簡(jiǎn)單,用了一個(gè)語法糖將內(nèi)部復(fù)雜的實(shí)現(xiàn)結(jié)結(jié)實(shí)實(shí)的包裝了起來。

其內(nèi)部可以用下面這張圖來概述:

在單核處理器的場(chǎng)景下,所有 goroutine 運(yùn)行在同一個(gè) M 系統(tǒng)線程中,每一個(gè) M 系統(tǒng)線程維護(hù)一個(gè) Processor,任何時(shí)刻,一個(gè) Processor 中只有一個(gè) goroutine,其他 goroutine 在 runqueue 中等待。一個(gè) goroutine 運(yùn)行完自己的時(shí)間片后,讓出上下文,回到 runqueue 中。 多核處理器的場(chǎng)景下,為了運(yùn)行 goroutines,每個(gè) M 系統(tǒng)線程會(huì)持有一個(gè) Processor 。

在正常情況下,scheduler 會(huì)按照上面的流程進(jìn)行調(diào)度,但是線程會(huì)發(fā)生阻塞等情況,看一下goroutine對(duì)線程阻塞等的處理。

到此這篇關(guān)于Go 并發(fā)編程協(xié)程及調(diào)度機(jī)制詳情的文章就介紹到這了,更多相關(guān)Go 調(diào)度機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言使用singleflight解決緩存擊穿

    Go語言使用singleflight解決緩存擊穿

    在構(gòu)建高性能的服務(wù)時(shí),緩存是優(yōu)化數(shù)據(jù)庫(kù)壓力和提高響應(yīng)速度的關(guān)鍵技術(shù),但使用緩存也會(huì)帶來一些問題,其中就包括緩存擊穿,下面我們就來看看Go語言中如何使用singleflight解決緩存擊穿問題吧
    2024-03-03
  • golang1.23版本之前 Timer Reset方法無法正確使用

    golang1.23版本之前 Timer Reset方法無法正確使用

    在Go 1.23之前,使用`time.Reset`函數(shù)時(shí)需要先調(diào)用`Stop`并明確從timer的channel中抽取出東西,以避免潛在的問題,然而,這在實(shí)際代碼中難以實(shí)現(xiàn),因?yàn)樵O(shè)置定時(shí)器狀態(tài)和發(fā)送channel的操作并不是原子的,在某些情況下,這會(huì)導(dǎo)致timer在不應(yīng)該觸發(fā)時(shí)提前觸發(fā)
    2025-01-01
  • 在golang xorm中使用postgresql的json,array類型的操作

    在golang xorm中使用postgresql的json,array類型的操作

    這篇文章主要介紹了在golang xorm中使用postgresql的json,array類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 使用gin框架搭建簡(jiǎn)易服務(wù)的實(shí)現(xiàn)方法

    使用gin框架搭建簡(jiǎn)易服務(wù)的實(shí)現(xiàn)方法

    go語言web框架挺多的,本文就介紹了一下如何使用gin框架搭建簡(jiǎn)易服務(wù)的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Go中使用操作符進(jìn)行數(shù)學(xué)運(yùn)算的示例代碼

    Go中使用操作符進(jìn)行數(shù)學(xué)運(yùn)算的示例代碼

    在編程中有效地執(zhí)行數(shù)學(xué)運(yùn)算是一項(xiàng)需要開發(fā)的重要技能,本文主要介紹了Go中使用操作符進(jìn)行數(shù)學(xué)運(yùn)算的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • 深入了解Golang中Slice切片的使用

    深入了解Golang中Slice切片的使用

    本文主要為大家詳細(xì)介紹了Golang中Slice切片的使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2023-02-02
  • Go語言使用Gob傳輸數(shù)據(jù)

    Go語言使用Gob傳輸數(shù)據(jù)

    本文主要介紹了Go語言使用Gob傳輸數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Golang迭代如何在Go中循環(huán)數(shù)據(jù)結(jié)構(gòu)使用詳解

    Golang迭代如何在Go中循環(huán)數(shù)據(jù)結(jié)構(gòu)使用詳解

    這篇文章主要為大家介紹了Golang迭代之如何在Go中循環(huán)數(shù)據(jù)結(jié)構(gòu)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 詳解Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理

    詳解Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理

    這篇文章主要為大家詳細(xì)介紹了Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下
    2023-11-11
  • Go語言字典(map)用法實(shí)例分析【創(chuàng)建,填充,遍歷,查找,修改,刪除】

    Go語言字典(map)用法實(shí)例分析【創(chuàng)建,填充,遍歷,查找,修改,刪除】

    這篇文章主要介紹了Go語言字典(map)用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Go語言字典的創(chuàng)建、填充、遍歷、查找、修改、刪除等操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-02-02

最新評(píng)論