一篇文章帶你入門Go語言基礎之并發(fā)
前言
Hey,大家好,我是碼農星期八,終于到了Go中最牛掰的地方,并發(fā),這也是Go為什么能快速火的原因。
部署方便,不需要容器,隨便跑一個都是相當于Nginx的存在,怎么肯能不火
所以,來看看吧!!!
引言
Go語言,專門為并發(fā)而生的語言,每啟動一個微線程創(chuàng)建一個代價大概2KB起步
假設一個內存條大小4G,一個微線程2kb,1G=1024M=1048576kb,1048576/2=524288,五十多萬個
但是你知道像Java,Python等語言,一個線程代價多大嗎???,2MB起步,代價直接翻了千倍
所以,激動吧,隨便用Go寫一個web程序,基本都相當于Nginx
goroutine
Go中的微線程,也叫做goroutine,goroutine是并行處理任務的
就像我用兩只手同時操作兩個手機打游戲一樣
而不是一個手玩玩這個,一個手玩玩那個,這樣切換式玩法
goroutine由Go的runtime完成調度,goroutine的本質是在代碼(用戶態(tài))級別完成的切換,代價很小
像Java,Python等語言的線程,是在操作系統(tǒng)(內核態(tài))級別完成的切花,所以代價非常大
由于goroutine是由runtime完成切換,并且runtime經過Google公司的數位大佬優(yōu)化,已經很小母牛上山了,牛逼哄哄了。
使用goroutine
在Go中使用goroutine很簡單,只需要在想調用的函數前加一個go就行了,這就代表啟動了一個goroutine
普通調用函數方式
函數
- func Say() {
- time.Sleep(time.Second)
- fmt.Println("我在說話說了1s說完了...")
- }
main
- func main() {
- //開始時間
- var start_time = time.Now()
- //啟動10個say說話
- for i := 0; i < 10; i++ {
- Say()
- }
- //結束時間
- var end_time = time.Now()
- //計算時間差
- fmt.Println(end_time.Sub(start_time))
- }
執(zhí)行結果

循環(huán)了10次,耗時10s,有點慢啊!
goroutine調用函數方式
函數還是上述的函數
main
- func main() {
- //開始時間
- var start_time = time.Now()
- //啟動10個say說話
- for i := 0; i < 10; i++ {
- go Say()
- }
- //結束時間
- var end_time = time.Now()
- //計算時間差
- fmt.Println(end_time.Sub(start_time))
- }
注意:第6行,前面加了個go關鍵字,go關鍵字就表示以一個微線程的方式單獨運行這個函數。
執(zhí)行結果

what??? 0s,什么情況?
為什么會出現0s這種情況
這是因為,在Go中,我們采用的是守護線程的方式,什么意思呢?

在Go中,main函數只要執(zhí)行完,其他微線程必涼。
就像有的怪獸,他們是互相依賴一個母體的,母體掛了,下面的娃也必掛。
所以該怎么解決這個問題呢???
sync.WaitGroup
上述我們發(fā)現,啟動了一些微線程,但是微線程還沒來得及執(zhí)行就掛了,是因為main函數跑的太快了,main跑完了,Go運行時自動將其他微線程關閉了。
那反過來想,我們如何讓main在最后等一下,等我的孩子們都回來了,我在繼續(xù)跑。
所以,有一個新的問題,那就是等,祭出法寶sync.WaitGroup
先看一下怎么用
函數
- func Say() {
- //函數結束時取消標記
- defer wg.Done()
- //每個函數在啟動時加上一個標記
- wg.Add(1)
- //函數開始打上一個標記
- time.Sleep(time.Second*1)
- fmt.Println("我在說話說了1s說完了...")
- }
main
- var wg sync.WaitGroup
- func main() {
- //開始時間
- var start_time = time.Now()
- //啟動10個say說話
- for i := 0; i < 10; i++ {
- go Say()
- }
- // 等待所有標記過的微線程執(zhí)行完畢
- wg.Wait()
- //結束時間
- var end_time = time.Now()
- //計算時間差
- fmt.Println(end_time.Sub(start_time))
- }
執(zhí)行結果

可以看到,10個線程同時啟動,1s就完了,并且代碼相對簡單,就算開啟10w個,還是1s多一點
這也是為什么很多公司越來越青睞Go的原因。

runtime.GOMAXPROCS
這個意思要使用多少個核,默認使用全部核心,性能跑滿,但是也有意外的情況,
比如一個機器跑了很多其他任務,Go寫的這個是不太重要的任務,但是是計算型的,這時候理論來說是不盡量擠兌別人的算力
所以要限制一下當前程序使用電腦的算力
代碼
- func main() {
- //本機的cpu個數
- var cpuNum = runtime.NumCPU()
- fmt.Println(cpuNum)
- //設置Go使用cpu個數
- runtime.GOMAXPROCS(4)
- }
總結
上述我們學習了Go的并發(fā),學習了
- 如何創(chuàng)建一個協(xié)程(goroutine)。
- 為什么需要sync.WaitGroup。
- 設置當前程序使用CPU核數。
在Go中,輕松實現一個高并發(fā)還是挺容易的,但是可能有些不是那么好理解。