示例剖析golang中的CSP并發(fā)模型
1. 相關(guān)概念:
用戶態(tài):當一個進程在執(zhí)行用戶自己的代碼時處于用戶運行態(tài)(用戶態(tài))
內(nèi)核態(tài):當一個進程因為系統(tǒng)調(diào)用陷入內(nèi)核代碼中執(zhí)行時處于內(nèi)核運行態(tài)(內(nèi)核態(tài)),引入內(nèi)核態(tài)防止用戶態(tài)的程序隨意的操作內(nèi)核地址空間,具有一定的安全保護作用。這種保護模式是通過內(nèi)存頁表操作等機制,保證進程間的地址空間不會相互沖突,一個進程的操作不會修改另一個進程地址空間中的數(shù)據(jù)。
用戶態(tài)與內(nèi)核態(tài)之間的切換:當在系統(tǒng)中執(zhí)行一個程序時,大部分時間都是運行在用戶態(tài)下的,在其需要操作系統(tǒng)幫助完成一些用戶態(tài)自己沒有特權(quán)和能力完成的操作時就會切換到內(nèi)核態(tài)。有以下三種方式:
(1)系統(tǒng)調(diào)用(中斷)用戶態(tài)進程主動要求切換到內(nèi)核態(tài)的一種方式。
(2)異常cpu運行時如果發(fā)生一些沒有預(yù)知的異常,會觸發(fā)當前進程切換到處理此異常的內(nèi)核相關(guān)進程中?!?/p>
(3)外圍設(shè)備的中斷用戶態(tài)進程主動要求切換到內(nèi)核態(tài)的一種方式。
協(xié)程:又稱微線程,纖程。英文名Coroutine。Coroutine是一種運行在用戶態(tài)的用戶線程,類似于 greenthread。協(xié)程與線程都相互獨立,且有自己的上下文,不同之處在于,協(xié)程的切換由其自身控制,而線程的切換收到系統(tǒng)調(diào)度。
2. CSP (通信順序進程)
CSP模型用來描述兩個獨立的并發(fā)實體通過共享的通訊channel管道進行通信的并發(fā)模型。
golang借用了CSP模型的一些概念如:實體 process,通道 channel,為之實現(xiàn)并發(fā)進行了理論支持,實際上并沒有完全實現(xiàn)CSP模型的所有理論。process是在go語言上的表現(xiàn)就是goroutine,是實際上并發(fā)執(zhí)行的實體,每個實體之間是通過channel通訊來實現(xiàn)數(shù)據(jù)共享。
3. channel:同步&傳遞消息
channel是被單獨創(chuàng)建并且可以在進程之間傳遞,它的通信模式類似于boss-worker模式,一個實體通過將消息發(fā)送到channel中,然后又監(jiān)聽這個channel的實體處理,兩個實體之間是匿名的,實現(xiàn)原理上其實是一個阻塞的消息隊列。
具體可以分為:有/無緩存channel,只讀channel,只寫channel,雙向channel
寫操作:chan <- value
讀操作:<- chan
// Create channel // Unbuffered channel umbuffer_chan := make(chan int) // Buffered channel // Buffer Size = 3 buffer_chan := make(chan int,3) // Read-Only channel read_channel := make(<-chan int) // Receive-Only channel receive_channel := make(chan<- int)
生產(chǎn)者-消費者Sample:
package main
import (
"fmt"
"time"
)
// 生產(chǎn)者
func Producer (queue chan<- int){
for i:= 0; i < 10; i++ {
queue <- i
}
}
// 消費者
func Consumer( queue <-chan int){
for i :=0; i < 10; i++{
v := <- queue
fmt.Println("receive:", v)
}
}
func main(){
queue := make(chan int, 1)
go Producer(queue)
go Consumer(queue)
time.Sleep(1e9) //讓Producer與Consumer完成
}4. goroutine:實際并發(fā)執(zhí)行的實體
在函數(shù)或者方法前面加上關(guān)鍵字go,就創(chuàng)建了并發(fā)運行的goroutine,eg:
go func (){
}
func Test(){
}
// ...
go Test()實例代碼:
package main // 代碼包聲明語句。
import (
"fmt" //系統(tǒng)包用來輸出的
"math/rand"
"runtime"
"sync"
"time"
)
func main() {
// 分配一個邏輯處理器給調(diào)度器使用
runtime.GOMAXPROCS(1)
// WaitGroup是一個計數(shù)信號量,用來記錄和維護運行的goroutine,如果當前的wg>0,對應(yīng)的exit方法就會阻塞
var wg sync.WaitGroup
// 計數(shù)加2表示要等待兩個goroutine
wg.Add(2)
fmt.Printf("Start Goroutines \n", )
// 聲明匿名函數(shù),創(chuàng)建goroutine
go func(){
// 關(guān)鍵字defer會修改函數(shù)調(diào)用時機,在函數(shù)退出時調(diào)用Done來通知main函數(shù)工作已經(jīng)完成
defer wg.Done()
for count:=0; count<3; count++ {
for char :='a'; char<'a'+26 ; char++ {
fmt.Printf("%c ", char)
}
}
}()
// 聲明匿名函數(shù),創(chuàng)建goroutine
go func() {
// 函數(shù)退出時調(diào)用Done來通知main函數(shù)工作已經(jīng)完成
defer wg.Done()
for count:=0; count<3; count++ {
for char :='A'; char<'A'+26 ; char++ {
fmt.Printf("%c ", char)
}
}
}()
fmt.Println("Waiting to finish!\n", )
// 等待結(jié)束
wg.Wait()
fmt.Println("\nTerminate program! \n", )
}5. golang調(diào)度器
OS在物理處理器上調(diào)度線程來運行,而golang在邏輯處理器上調(diào)度goroutine來運行。每個邏輯處理器都分別綁定到單個操作系統(tǒng)線程。
如果創(chuàng)建一個goroutine并準備運行,這個goroutine就會被放到調(diào)度器的全局運行隊列中。之后,調(diào)度器就會將隊列中的goroutine分配給一個邏輯處理器,并放到這個邏輯處理器對應(yīng)的本地運行隊列中。本地運行隊列中的goroutine會一直等待,知道自己被分配到相應(yīng)的邏輯處理器上運行。
eg:

其中:
M:Machine,一個M直接關(guān)聯(lián)了一個內(nèi)核線程。
P:Processor,代表了M所需要的上下文環(huán)境,也就是處理用戶級代碼邏輯的處理器。
G:Goroutine,本質(zhì)上是一種輕量級的線程--協(xié)程。
MPG模型,三者關(guān)系的宏觀圖為:

Processor的作用:
當內(nèi)核線程阻塞的時候,由于上下文的存在,我們能夠直接放開其他線程,繼續(xù)去執(zhí)行未阻塞的線程,例子如下:

如果當前,G0由于I/O,系統(tǒng)調(diào)用進行了阻塞,這個時候M0就可以放開其他的線程:

M0和G0進行系統(tǒng)調(diào)用,等待返回值,上下文P以及routine隊列交由M1進行執(zhí)行。當M0執(zhí)行系統(tǒng)調(diào)用結(jié)束后,M0會嘗試去steal("偷")一個上下文,如果不成功,M0就把它的G0放到一個全局的運行隊列中,然后將自己放到線程池或者轉(zhuǎn)入休眠狀態(tài)。
Global runqueue是各個上下文P在運行完自己的本地的goroutine runqueue后用來拉取新的goroutine的地方(steal working算法)。此外,P也會周期性的檢查Global runqueue上的goroutine,來防止全局上的goroutine因為得不到執(zhí)行而餓死。
以上就是示例剖析golang中的CSP并發(fā)模型的詳細內(nèi)容,更多關(guān)于golang CSP并發(fā)模型的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go?time.Sleep睡眠指定時間實例詳解(小時級到納秒級)
golang的休眠可以使用time包中的sleep,下面這篇文章主要給大家介紹了關(guān)于go?time.Sleep睡眠指定時間(小時級到納秒級)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-11-11
golang執(zhí)行命令操作 exec.Command
這篇文章主要介紹了golang執(zhí)行命令操作 exec.Command,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
gin自定義中間件解決requestBody不可重讀(請求體取值)
這篇文章主要介紹了gin自定義中間件解決requestBody不可重讀,確??刂破髂軌颢@取請求體值,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10

