網(wǎng)站首頁 編程語言 正文
GO的并發(fā)編程分享
之前我們分享了網(wǎng)絡(luò)編程,今天我們來看看GO的并發(fā)編程分享,我們先來看看他是個啥
啥是并發(fā)編程呢
指在一臺處理器上同時處理多個任務(wù)
此處說的同時,可不是同一個時間一起手拉手做同一件事情
并發(fā)是在同一實體上的多個事件,而這個事件在同一時間間隔發(fā)生的,同一個時間段,有多個任務(wù)執(zhí)行,可是同一個時間點,只有一個任務(wù)在執(zhí)行
為啥要有并發(fā)編程
隨著互聯(lián)網(wǎng)的普及,互聯(lián)網(wǎng)用戶人數(shù)原來越多,這對系統(tǒng)的性能帶來了巨大的挑戰(zhàn)。
我們要通過各種方式來高效利用硬件的性能(壓榨),從而提高系統(tǒng)的性能進(jìn)而提升用戶體驗,提升團(tuán)隊或者企業(yè)的競爭力。
并發(fā)是為了解決什么問題?目的是啥?
是充分的利用好處理器的每一個核,以達(dá)到最高的處理性能,盡可能的運用好每一塊磚
可是由于現(xiàn)在我們使用的CPU,內(nèi)存,IO三者之間速度不盡相同
我們?yōu)榱颂岣呦到y(tǒng)性能,計算機(jī)系統(tǒng)會將這三者速度進(jìn)行平衡,以達(dá)到最優(yōu)的效果,都有如下措施:
- 操作系統(tǒng)增加了進(jìn)程、線程,以分時復(fù)用 CPU,進(jìn)而均衡 CPU 與 I/O 設(shè)備的速度差異;
- CPU 增加了緩存,以均衡與內(nèi)存的速度差異;
- 編譯程序優(yōu)化指令執(zhí)行次序,使得緩存能夠得到更加合理地利用。
說到進(jìn)程和線程,他們都是干啥的呢,咱們順帶說一下?
進(jìn)程是程序在操作系統(tǒng)中的一次執(zhí)行過程
是 系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨立單位。
線程是進(jìn)程的一個執(zhí)行實體
是 CPU 調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨立運行的基本單位。
一個進(jìn)程可以創(chuàng)建和撤銷多個線程, 并且同一個進(jìn)程中的多個線程之間可以并發(fā)執(zhí)行。
講到并發(fā)編程不得不說并發(fā)和并行有啥區(qū)別?是不是總是有小伙伴弄不清楚他們到底是啥區(qū)別,好像一樣,又好像不一樣
并發(fā)和并行的區(qū)別
一言蔽之,區(qū)別如下:
并發(fā)
多線程程序在一個核的 CPU 上運行
并行
多線程程序在多個核的 CPU 上運行
并發(fā)就像多個小伙伴跑接力,同一個時間點只會有一個小伙伴在跑,互相有影響
并行就像是多個小伙伴同一個起點一起跑,互不干擾
我們需要記住一點,再強(qiáng)調(diào)一波:
并發(fā)不是并行
并發(fā)主要由切換時間片來實現(xiàn)"同時"運行
并行則是直接利用多核實現(xiàn)多線程的運行,
在 GO 可以設(shè)置使用核數(shù),以發(fā)揮多核計算機(jī)的能力,不過設(shè)置核數(shù)都是依賴于硬件的
那么,講到GO的并發(fā)編程,就必須上我們的主角,那就是協(xié)程
協(xié)程 goroutine 是啥
協(xié)程是一種程序組件
是由子例程(過程、函數(shù)、例程、方法、子程序)的概念泛化而來的
子例程只有一個入口點且只返回一次,而協(xié)程允許多個入口點,可以在指定位置掛起和恢復(fù)執(zhí)行。
協(xié)程和線程分別有啥特點嘞
協(xié)程
獨立的??臻g,共享堆空間,調(diào)度由用戶自己控制
本質(zhì)上有點類似于用戶級線程,這些用戶級線程的調(diào)度也是自己實現(xiàn)的。
線程
一個線程上可以跑多個協(xié)程,協(xié)程是輕量級的線程。
GO 高并發(fā)的原因是啥
-
goroutine
奉行通過通信來共享內(nèi)存 - 每個一個GO的實例有
4~5KB
的棧內(nèi)存占用,并且由于 GO 實現(xiàn)機(jī)制而大幅減少的創(chuàng)建和銷毀開銷 - Golang 在語言層面上就支持協(xié)程
goroutine
GOLANG并發(fā)編程涉及哪些知識點呢
- 基本協(xié)程的原理,實現(xiàn)方式,雖然說,GO中使用協(xié)程很方便,可以我們必須要知其然而值其所以然
- Goroutine 池
- runtime 包的使用
- Channel 通道
- 定時器
- 并發(fā)且安全的鎖
- 原子操作
- select 多路復(fù)用
- 等等...
Goroutine的那些事
我們寫C/C++
的時候,我們必然也是要實現(xiàn)并發(fā)編程
我們通常需要自己維護(hù)一個線程池,并且需要自己去包裝一個又一個的任務(wù),同時需要自己去調(diào)度線程執(zhí)行任務(wù)并維護(hù)上下文切換
且做線程池的時候,我們需要自己做一個線程管理的角色,靈活動態(tài)壓縮和擴(kuò)容
可是能不能有這樣一種機(jī)制,我們只需要定義多個任務(wù),讓系統(tǒng)去幫助我們把這些任務(wù)分配到CPU上實現(xiàn)并發(fā)執(zhí)行
GO里面就正好有這樣的機(jī)制
goroutine
的概念類似于線程
goroutine
是由Go的運行時(runtime)調(diào)度和管理的
Go程序會智能地將 goroutine
中的任務(wù)合理地分配給每個CPU
Go 在語言層面已經(jīng)內(nèi)置了調(diào)度和上下文切換的機(jī)制
寫 GO 比較爽的一個地方是:
在GO里面,你不需要去自己寫進(jìn)程、線程、協(xié)程
我們可以使用 goroutine 包
如何使用 goroutine
我們需要讓某個任務(wù)并發(fā)執(zhí)行的時候,只需要把這個任務(wù)包裝成一個函數(shù)
專門開啟一個 goroutine 協(xié)程 去執(zhí)行這個函數(shù)就可以了 , GO一個協(xié)程,很方便
一個 goroutine 必定對應(yīng)一個函數(shù),可以創(chuàng)建多個 goroutine 去執(zhí)行相同的函數(shù),只是多個協(xié)程都是做同一個事情罷了
我們先來使用一下協(xié)程,再來拋磚引玉,適當(dāng)?shù)姆窒硪幌?/p>
啟動單個協(xié)程
func Hi() { fmt.Println("this is Hi Goroutine!") } func main() { Hi() fmt.Println("main goroutine!") }
我們一般調(diào)用函數(shù)是如上這個樣子的,效果如下
this is Hi Goroutine!
main goroutine!
其實我們調(diào)用協(xié)程的話,也與上述類似
我們可以使用 go 后面加上函數(shù)名字,來開辟一個協(xié)程,專門做函數(shù)需要執(zhí)行的事情
func main() { go Hi() // 啟動一個goroutine 協(xié)程 去執(zhí)行 Hi 函數(shù) fmt.Println("main goroutine!")
實際效果我們可以看到,程序只打印了 main goroutine!
main goroutine!
在程序啟動的時候,Go 程序就會為 main() 函數(shù)創(chuàng)建一個默認(rèn)的 goroutine 協(xié)程
當(dāng) main() 函數(shù)返回的時候,剛開辟的另外一個 goroutine 協(xié)程 就結(jié)束了
所有在 main() 函數(shù)中啟動的 goroutine 協(xié)程 會一同結(jié)束,老大死了,其余的傀儡也灰飛煙滅了
我們也可以讓主協(xié)程等等一定子協(xié)程,待子協(xié)程處理完自己的事情,退出后,主協(xié)程再自己退出,這和我們寫C/C++的進(jìn)程 和 線程的時候,類似
簡單的,我們可以使用 time.sleep
函數(shù)來讓主協(xié)程阻塞等待
我們也可以使用 上述提到的 使用 select{} 來達(dá)到目的
當(dāng)然也有其他的方式,后續(xù)文章會慢慢的分享到
多個協(xié)程
那么多個協(xié)程又是怎么玩的呢?
我們使用 sync.WaitGroup 來實現(xiàn)goroutine 協(xié)程的同步
package main import ( "fmt" "sync" ) var myWg sync.WaitGroup func Hi(i int) { // goroutine 協(xié)程 結(jié)束就 記錄 -1 defer myWg.Done() fmt.Println("Hello Goroutine! the ", i) } func main() { for i := 0; i < 10; i++ { // 啟動一個goroutine 協(xié)程 就記錄 +1 myWg.Add(1) go Hi(i) } // 等待所有記錄 的goroutine 協(xié)程 都結(jié)束 myWg.Wait() }
會有如下輸出,每一個協(xié)程打印的數(shù)字并不是按照順序來的:
Hello Goroutine! the ?9
Hello Goroutine! the ?4
Hello Goroutine! the ?2
Hello Goroutine! the ?3
Hello Goroutine! the ?6
Hello Goroutine! the ?5
Hello Goroutine! the ?7
Hello Goroutine! the ?8
Hello Goroutine! the ?1
Hello Goroutine! the ?0
還是同樣的, 如果是主協(xié)程先退出,那么子協(xié)程還行繼續(xù)運行嗎?
毋庸置疑,主協(xié)程退出,子協(xié)程也會跟著退出
GO 中的協(xié)程
分享如下幾個點
GO中的棧是可增長的
一般都有固定的棧內(nèi)存(通常為2MB),goroutine 的棧不是固定的,goroutine 的棧大小可以擴(kuò)展到1GB
goroutine 是如何調(diào)度
這就不得不提 GPM
GPM是Go語言運行時(runtime)層面實現(xiàn)的,我們先簡單了解一下GPM分別代表啥
G
就是個 goroutine ,里面除了存放本 goroutine 信息外 還有與所在P的綁定等信息
P
Processor 管理著一組 goroutine 隊列
P 里面會存儲當(dāng)前 goroutine 運行的上下文環(huán)境(函數(shù)指針,堆棧地址及地址邊界)
P 會對自己管理的 goroutine 隊列做一些調(diào)度(比如把占用CPU時間較長的 goroutine 暫停、運行后續(xù)的 goroutine)
當(dāng)自己的隊列消費完了就去全局隊列里取,如果全局隊列里也消費完了會去其他P的隊列里搶任務(wù)。
M(machine)
是 Go 運行時(runtime)對操作系統(tǒng)內(nèi)核線程的虛擬
M 與內(nèi)核線程一般是一一映射的關(guān)系, 一個 groutine 最終是要放到 M上執(zhí)行
這里的 P 與 M 一般也是一一對應(yīng)的
P 管理著一組G 掛載在 M 上運行
當(dāng)一個 G 長久阻塞在一個 M 上時,runtime 會新建一個M,
阻塞 G 所在的 P 會把其他的 G 掛載在新建的M上
這個時候,當(dāng)舊的 G 阻塞完成或者認(rèn)為其已經(jīng)掛了的話,就會回收舊的 M
還有一點
P 的個數(shù)是通過 runtime.GOMAXPROCS
設(shè)定(最大256),這個數(shù)字也依賴于自己的硬件,在并發(fā)量大的時候會增加一些 P 和 M ,但不會太多
總結(jié)
- 分享了并發(fā)和并行
- 分享了GO 的并發(fā),協(xié)程的簡單使用
- 簡單分享了GO可伸縮擴(kuò)展的棧內(nèi)存
原文鏈接:https://juejin.cn/post/6972547346039046157
- 上一篇:沒有了
- 下一篇:沒有了
相關(guān)推薦
- 2022-09-14 Android多渠道打包神器ProductFlavor詳解_Android
- 2022-04-19 Golang語言的多種變量聲明方式與使用場景詳解_Golang
- 2022-05-31 C#實現(xiàn)WPF項目復(fù)制和移動文件夾_C#教程
- 2022-04-23 R語言繪制line?plot線圖示例詳解_R語言
- 2022-10-31 Kotlin函數(shù)式編程超詳細(xì)介紹_Android
- 2022-12-21 Kotlin?launch原理全面分析_Android
- 2022-11-27 C語言中花式退出程序的方式總結(jié)_C 語言
- 2022-12-21 Android?RecyclerView四級緩存源碼層詳細(xì)分析_Android
- 欄目分類
-
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支