一篇文章帶你入門 Go 語言基礎之併發
前言
Hey,大家好,我是碼農星期八,終於到了 Go 中最牛掰的地方,併發,這也是 Go 爲什麼能快速火的原因。
部署方便,不需要容器,隨便跑一個都是相當於 Nginx 的存在,怎麼肯能不火
所以,來看看扒!!!
引言
Go 語言,專門爲併發而生的語言,每啓動一個微線程創建一個代價大概2KB
起步
假設一個內存條大小 4G,一個微線程 2kb,1G=1024M=1048576kb
,1048576/2=524288
,五十多萬個
但是你知道像 Java,Python 等語言,一個線程代價多大嗎???,2MB
起步,代價直接翻了千倍
所以,激動吧,隨便用 Go 寫一個 web 程序,基本都相當於Nginx
goroutine
Go 中的微線程,也叫做goroutine
,goroutine
是並行處理任務的
就像我用兩隻手同時操作兩個手機打遊戲一樣
而不是一個手玩玩這個,一個手玩玩那個,這樣切換式玩法
goroutine
由 Go 的runtime
完成調度,goroutine
的本質是在代碼 (用戶態) 級別完成的切換,代價很小
像 Java,Python 等語言的線程,是在操作系統 (內核態) 級別完成的切花,所以代價非常大
由於goroutine
是由runtime
完成切換,並且runtime
經過 Google 公司的數位大佬優化,已經很小母牛上山了,牛逼哄哄了。
使用 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))
}
執行結果
循環了 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 關鍵字就表示以一個微線程的方式單獨運行這個函數。
執行結果
what??? 0s,什麼情況?
爲什麼會出現 0s 這種情況
這是因爲,在 Go 中,我們採用的是守護線程的方式,什麼意思呢?
在 Go 中,main 函數只要執行完,其他微線程必涼。
就像有的怪獸,他們是互相依賴一個母體的,母體掛了,下面的娃也必掛。
所以該怎麼解決這個問題呢???
sync.WaitGroup
上述我們發現,啓動了一些微線程,但是微線程還沒來得及執行就掛了,是因爲 main 函數跑的太快了,main 跑完了,Go 運行時自動將其他微線程關閉了。
那反過來想,我們如何讓 main 在最後等一下,等我的孩子們都回來了,我在繼續跑。
所以,有一個新的問題,那就是等,祭出法寶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()
}
// 等待所有標記過的微線程執行完畢
wg.Wait()
//結束時間
var end_time = time.Now()
//計算時間差
fmt.Println(end_time.Sub(start_time))
}
執行結果
可以看到,10 個線程同時啓動,1s 就完了,並且代碼相對簡單,就算開啓 10w 個,還是 1s 多一點
這也是爲什麼很多公司越來越青睞 Go 的原因。
runtime.GOMAXPROCS
這個意思要使用多少個核,默認使用全部核心,性能跑滿,但是也有意外的情況,
比如一個機器跑了很多其他任務,Go 寫的這個是不太重要的任務,但是是計算型的,這時候理論來說是不盡量擠兌別人的算力
所以要限制一下當前程序使用電腦的算力
代碼
func main() {
//本機的cpu個數
var cpuNum = runtime.NumCPU()
fmt.Println(cpuNum)
//設置Go使用cpu個數
runtime.GOMAXPROCS(4)
}
總結
上述我們學習了 Go 的併發,學習了
-
如何創建一個協程 (goroutine)。
-
爲什麼需要
sync.WaitGroup
。 -
設置當前程序使用 CPU 核數。
在 Go 中,輕鬆實現一個高併發還是挺容易的,但是可能有些不是那麼好理解。
如果在操作過程中有任何問題,記得下面留言,我們看到會第一時間解決問題。
我是碼農星期八,如果覺得還不錯,記得動手點贊一下哈。
感謝你的觀看。
想了解更多關於 Go 的知識,請前往:http://pdcfighting.com/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/f5A9w7WCxXj8LXsnVRiMEg