一篇文章帶你入門 Go 語言基礎之併發

前言

Hey,大家好,我是碼農星期八,終於到了 Go 中最牛掰的地方,併發,這也是 Go 爲什麼能快速火的原因。

部署方便,不需要容器,隨便跑一個都是相當於 Nginx 的存在,怎麼肯能不火

所以,來看看扒!!!

引言

Go 語言,專門爲併發而生的語言,每啓動一個微線程創建一個代價大概2KB起步

假設一個內存條大小 4G,一個微線程 2kb1G=1024M=1048576kb1048576/2=524288,五十多萬個

但是你知道像 Java,Python 等語言,一個線程代價多大嗎???,2MB起步,代價直接翻了千倍

所以,激動吧,隨便用 Go 寫一個 web 程序,基本都相當於Nginx

goroutine

Go 中的微線程,也叫做goroutinegoroutine是並行處理任務的

就像我用兩隻手同時操作兩個手機打遊戲一樣

而不是一個手玩玩這個,一個手玩玩那個,這樣切換式玩法

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 的併發,學習了

在 Go 中,輕鬆實現一個高併發還是挺容易的,但是可能有些不是那麼好理解。

如果在操作過程中有任何問題,記得下面留言,我們看到會第一時間解決問題。

我是碼農星期八,如果覺得還不錯,記得動手點贊一下哈。

感謝你的觀看。

想了解更多關於 Go 的知識,請前往:http://pdcfighting.com/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/f5A9w7WCxXj8LXsnVRiMEg