瞅一眼就能學會的GO并發(fā)編程使用教程
GO的并發(fā)編程分享
之前我們分享了網絡編程,今天我們來看看GO的并發(fā)編程分享,我們先來看看他是個啥
啥是并發(fā)編程呢
指在一臺處理器上同時處理多個任務
此處說的同時,可不是同一個時間一起手拉手做同一件事情
并發(fā)是在同一實體上的多個事件,而這個事件在同一時間間隔發(fā)生的,同一個時間段,有多個任務執(zhí)行,可是同一個時間點,只有一個任務在執(zhí)行
為啥要有并發(fā)編程
隨著互聯(lián)網的普及,互聯(lián)網用戶人數(shù)原來越多,這對系統(tǒng)的性能帶來了巨大的挑戰(zhàn)。
我們要通過各種方式來高效利用硬件的性能(壓榨),從而提高系統(tǒng)的性能進而提升用戶體驗,提升團隊或者企業(yè)的競爭力。
并發(fā)是為了解決什么問題?目的是啥?
是充分的利用好處理器的每一個核,以達到最高的處理性能,盡可能的運用好每一塊磚
可是由于現(xiàn)在我們使用的CPU,內存,IO三者之間速度不盡相同
我們?yōu)榱颂岣呦到y(tǒng)性能,計算機系統(tǒng)會將這三者速度進行平衡,以達到最優(yōu)的效果,都有如下措施:
- 操作系統(tǒng)增加了進程、線程,以分時復用 CPU,進而均衡 CPU 與 I/O 設備的速度差異;
- CPU 增加了緩存,以均衡與內存的速度差異;
- 編譯程序優(yōu)化指令執(zhí)行次序,使得緩存能夠得到更加合理地利用。
說到進程和線程,他們都是干啥的呢,咱們順帶說一下?
進程是程序在操作系統(tǒng)中的一次執(zhí)行過程
是 系統(tǒng)進行資源分配和調度的一個獨立單位。
線程是進程的一個執(zhí)行實體
是 CPU 調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
一個進程可以創(chuàng)建和撤銷多個線程, 并且同一個進程中的多個線程之間可以并發(fā)執(zhí)行。
講到并發(fā)編程不得不說并發(fā)和并行有啥區(qū)別?是不是總是有小伙伴弄不清楚他們到底是啥區(qū)別,好像一樣,又好像不一樣
并發(fā)和并行的區(qū)別
一言蔽之,區(qū)別如下:
并發(fā)
多線程程序在一個核的 CPU 上運行
并行
多線程程序在多個核的 CPU 上運行
并發(fā)就像多個小伙伴跑接力,同一個時間點只會有一個小伙伴在跑,互相有影響
并行就像是多個小伙伴同一個起點一起跑,互不干擾
我們需要記住一點,再強調一波:
并發(fā)不是并行
并發(fā)主要由切換時間片來實現(xiàn)"同時"運行
并行則是直接利用多核實現(xiàn)多線程的運行,
在 GO 可以設置使用核數(shù),以發(fā)揮多核計算機的能力,不過設置核數(shù)都是依賴于硬件的
那么,講到GO的并發(fā)編程,就必須上我們的主角,那就是協(xié)程
協(xié)程 goroutine 是啥
協(xié)程是一種程序組件
是由子例程(過程、函數(shù)、例程、方法、子程序)的概念泛化而來的
子例程只有一個入口點且只返回一次,而協(xié)程允許多個入口點,可以在指定位置掛起和恢復執(zhí)行。
協(xié)程和線程分別有啥特點嘞
協(xié)程
獨立的棧空間,共享堆空間,調度由用戶自己控制
本質上有點類似于用戶級線程,這些用戶級線程的調度也是自己實現(xiàn)的。
線程
一個線程上可以跑多個協(xié)程,協(xié)程是輕量級的線程。
GO 高并發(fā)的原因是啥
goroutine
奉行通過通信來共享內存- 每個一個GO的實例有
4~5KB
的棧內存占用,并且由于 GO 實現(xiàn)機制而大幅減少的創(chuàng)建和銷毀開銷 - Golang 在語言層面上就支持協(xié)程
goroutine
GOLANG并發(fā)編程涉及哪些知識點呢
- 基本協(xié)程的原理,實現(xiàn)方式,雖然說,GO中使用協(xié)程很方便,可以我們必須要知其然而值其所以然
- Goroutine 池
- runtime 包的使用
- Channel 通道
- 定時器
- 并發(fā)且安全的鎖
- 原子操作
- select 多路復用
- 等等...
Goroutine的那些事
我們寫C/C++
的時候,我們必然也是要實現(xiàn)并發(fā)編程
我們通常需要自己維護一個線程池,并且需要自己去包裝一個又一個的任務,同時需要自己去調度線程執(zhí)行任務并維護上下文切換
且做線程池的時候,我們需要自己做一個線程管理的角色,靈活動態(tài)壓縮和擴容
可是能不能有這樣一種機制,我們只需要定義多個任務,讓系統(tǒng)去幫助我們把這些任務分配到CPU上實現(xiàn)并發(fā)執(zhí)行
GO里面就正好有這樣的機制
goroutine
的概念類似于線程
goroutine
是由Go的運行時(runtime)調度和管理的
Go程序會智能地將 goroutine
中的任務合理地分配給每個CPU
Go 在語言層面已經內置了調度和上下文切換的機制
寫 GO 比較爽的一個地方是:
在GO里面,你不需要去自己寫進程、線程、協(xié)程
我們可以使用 goroutine 包
如何使用 goroutine
我們需要讓某個任務并發(fā)執(zhí)行的時候,只需要把這個任務包裝成一個函數(shù)
專門開啟一個 goroutine 協(xié)程 去執(zhí)行這個函數(shù)就可以了 , GO一個協(xié)程,很方便
一個 goroutine 必定對應一個函數(shù),可以創(chuàng)建多個 goroutine 去執(zhí)行相同的函數(shù),只是多個協(xié)程都是做同一個事情罷了
我們先來使用一下協(xié)程,再來拋磚引玉,適當?shù)姆窒硪幌?/p>
啟動單個協(xié)程
func Hi() { fmt.Println("this is Hi Goroutine!") } func main() { Hi() fmt.Println("main goroutine!") }
我們一般調用函數(shù)是如上這個樣子的,效果如下
this is Hi Goroutine!
main goroutine!
其實我們調用協(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)建一個默認的 goroutine 協(xié)程
當 main() 函數(shù)返回的時候,剛開辟的另外一個 goroutine 協(xié)程 就結束了
所有在 main() 函數(shù)中啟動的 goroutine 協(xié)程 會一同結束,老大死了,其余的傀儡也灰飛煙滅了
我們也可以讓主協(xié)程等等一定子協(xié)程,待子協(xié)程處理完自己的事情,退出后,主協(xié)程再自己退出,這和我們寫C/C++的進程 和 線程的時候,類似
簡單的,我們可以使用 time.sleep
函數(shù)來讓主協(xié)程阻塞等待
我們也可以使用 上述提到的 使用 select{} 來達到目的
當然也有其他的方式,后續(xù)文章會慢慢的分享到
多個協(xié)程
那么多個協(xié)程又是怎么玩的呢?
我們使用 sync.WaitGroup 來實現(xiàn)goroutine 協(xié)程的同步
package main import ( "fmt" "sync" ) var myWg sync.WaitGroup func Hi(i int) { // goroutine 協(xié)程 結束就 記錄 -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é)程 都結束 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中的棧是可增長的
一般都有固定的棧內存(通常為2MB),goroutine 的棧不是固定的,goroutine 的棧大小可以擴展到1GB
goroutine 是如何調度
這就不得不提 GPM
GPM是Go語言運行時(runtime)層面實現(xiàn)的,我們先簡單了解一下GPM分別代表啥
G
就是個 goroutine ,里面除了存放本 goroutine 信息外 還有與所在P的綁定等信息
P
Processor 管理著一組 goroutine 隊列
P 里面會存儲當前 goroutine 運行的上下文環(huán)境(函數(shù)指針,堆棧地址及地址邊界)
P 會對自己管理的 goroutine 隊列做一些調度(比如把占用CPU時間較長的 goroutine 暫停、運行后續(xù)的 goroutine)
當自己的隊列消費完了就去全局隊列里取,如果全局隊列里也消費完了會去其他P的隊列里搶任務。
M(machine)
是 Go 運行時(runtime)對操作系統(tǒng)內核線程的虛擬
M 與內核線程一般是一一映射的關系, 一個 groutine 最終是要放到 M上執(zhí)行
這里的 P 與 M 一般也是一一對應的
P 管理著一組G 掛載在 M 上運行
當一個 G 長久阻塞在一個 M 上時,runtime 會新建一個M,
阻塞 G 所在的 P 會把其他的 G 掛載在新建的M上
這個時候,當舊的 G 阻塞完成或者認為其已經掛了的話,就會回收舊的 M
還有一點
P 的個數(shù)是通過 runtime.GOMAXPROCS
設定(最大256),這個數(shù)字也依賴于自己的硬件,在并發(fā)量大的時候會增加一些 P 和 M ,但不會太多
總結
- 分享了并發(fā)和并行
- 分享了GO 的并發(fā),協(xié)程的簡單使用
- 簡單分享了GO可伸縮擴展的棧內存
到此這篇關于瞅一眼就能學會的GO并發(fā)編程使用教程的文章就介紹到這了,更多相關GO并發(fā)編程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言驅動低代碼應用引擎工具Yao開發(fā)管理系統(tǒng)
這篇文章主要為大家介紹了Go語言驅動低代碼應用引擎工具Yao開發(fā)管理系統(tǒng)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06Golang中基礎的命令行模塊urfave/cli的用法說明
這篇文章主要介紹了Golang中基礎的命令行模塊urfave/cli的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12Golang并發(fā)讀取文件數(shù)據(jù)并寫入數(shù)據(jù)庫的項目實踐
本文主要介紹了Golang并發(fā)讀取文件數(shù)據(jù)并寫入數(shù)據(jù)庫的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-06-06