Go 語言併發編程保姆級教程!
哈嘍,大家好,我是 Go 大叔,專注分享 Go 語言知識,一起進入 Go 的大門。
大叔和身邊一羣大牛都無限看好 Go 語言,現在開始搞 Go 語言,過兩年大概就是第一批喫螃蟹的人。
併發編程基本概念
-
學習併發編程之前我們需要腦補幾個基礎知識和思考一個問題
-
什麼是串行?
-
什麼是並行?
-
什麼是併發?
-
什麼是程序?
-
什麼是進程?
-
什麼是線程?
-
什麼是協程?
什麼是串行?
-
串行就是按順序執行, 就好比銀行只有 1 個窗口, 有 3 個人要辦事, 那麼必須排隊, 只有前面的人辦完走人, 才能輪到你
-
在計算機中, 同一時刻, 只能有一條指令, 在一個 CPU 上執行, 後面的指令必須等到前面指令執行完才能執行, 就是串行 +
什麼是並行?
-
並行就是同時執行, 就好比銀行有 3 個窗口, 有 3 個人要辦事, 只需要到空窗口即可立即辦事.
-
在計算機中, 同一時刻, 有多條指令, 在多個 CPU 上執行, 就是並行
-
從以上分析不難看出, 並行的速度優於串行 +
什麼是併發?
-
併發是僞並行, 就好比銀行只有 1 個窗口, 有 3 個人要辦事, 那麼沒輪到後面的人時, 後面的人可以用拖鞋先排隊, 去喫個早餐, 買個東西啥的, 感覺差不多要到自己時再回來辦事
-
在計算機中, _同一時刻, 只能有一條指令, 在一個 CPU 上執行, 但是 CPU 會快速的在多條指令之間輪詢執行_就是併發
-
並行和併發的區別就好比古代的三妻四妾 (名正言順, 光明正大) 和現代三妻四妾(抽空幽會, 小三小四)
-
-
-
總結:
-
多線程程序在單核上運行, 就是併發
-
多線程程序在多核上運行, 就是並行
什麼是程序?
-
程序
是指將編譯型語言
編寫好的代碼通過編譯工具編譯之後存儲在硬盤
上的一個二進制文件
, 會佔用磁盤空間, 但不會佔用系統資源 -
例如我們通過 C++ 編寫了一個聊天程序, 然後通過 C++ 編譯器將編寫好的代碼編譯成一個二進制的文件, 那麼這個二進制的文件就是一個程序
什麼是進程?
-
進程
是指程序
在操作系統中的一次執行過程, 是系統進行資源分配和調度的基本單位 -
例如:
-
啓動記事本這個程序, 在系統中就會創建一個記事本進程
-
再次啓動記事本這個程序, 又會在系統中創建一個記事本進程
-
程序和進程的關係就好比劇本和演出的關係
-
劇本對應程序, 演出對應進程. 同一個劇本可以在多個舞臺同時演出互不影響, 同一個程序可以在系統中開啓多個進程互不影響
-
所以程序和進程的關係是 1:N, 所以多個進程的空間是獨立的
什麼是線程?
-
線程是指進程中的一個執行實例, 是程序執行的最小單元, 它是比進程更小的能獨立運行的基本單位
-
一個進程中至少有一個線程, 這個線程我們稱之爲
主線程
-
一個進程中除了
主線程
以外, 我們還可以創建和銷燬多個線程 -
例如:
-
啓動迅雷這個程序, 系統會創建一個
迅雷進程
, 並且默認會有一個主線程
, 用於執行迅雷默認的業務邏輯 -
當我們利用迅雷下載
多個任務
的時候, 會發現多個任務都在同時下載
, 此時爲了能夠同時執行
下載操作, 迅雷就會創建多個線程, 將不同的下載任務放到不同的線程中執行 -
什麼是協程?
-
協程是一種用戶態的輕量級線程,又稱微線程,英文名 Coroutine
-
與傳統的系統級別進程和線程相比, 協程最大的優勢在於 "輕量級". 可以輕鬆創建上萬個不會導致系統資源衰竭. 而線程和進程通常很難超過 1 萬個. 這也是協程稱之爲 "輕量級線程" 的原因
-
一個線程中可以有任意多個協程, 但
某一時刻只能有一個協程在運行
, 多個協程分享所在線程分配到的計算機資源 -
-
在協程中, 調用一個任務就像調用一個函數一樣, 消耗系統資源極少, 但能達到進程、線程相同的併發效果
Go 併發
-
Go 在語言級別支持
協程
(多數語言在語法層面並不直接支持協程), 叫做 goroutine. -
人們把 Go 語言稱之爲 21 世紀的 C 語言. 第一是因爲 Go 語言設計簡單, 第二是因爲 21 世紀最重要的就是並行程序設計. 而 Go 從語言層面就支持併發和並行
-
Go 併發小案例
package main
import (
"fmt"
"time"
)
func sing() {
for i:=0; i< 10; i++{
fmt.Println("我在唱歌")
time.Sleep(time.Millisecond)
}
}
func dance() {
for i:=0; i< 10; i++{
fmt.Println("我在跳舞---")
time.Sleep(time.Millisecond)
}
}
func main() {
// 串行: 必須先唱完歌才能跳舞
//sing()
//dance()
// 並行: 可以邊唱歌, 邊跳舞
// 注意點: 主線程不能死, 否則程序就退出了
go sing() // 開啓一個協程
go dance() // 開啓一個協程
for{
;
}
}
-
runtime 包中常用的函數
-
Gosched: 使當前 go 程放棄處理器,以讓其它 go 程運行
package main
import (
"fmt"
"runtime"
)
func sing() {
for i:=0; i< 10; i++{
fmt.Println("我在唱歌")
// Gosched使當前go程放棄處理器,以讓其它go程運行。
// 它不會掛起當前go程,因此當前go程未來會恢復執行
runtime.Gosched()
}
}
func dance() {
for i:=0; i< 10; i++{
fmt.Println("我在跳舞---")
runtime.Gosched()
}
}
func main() {
go sing()
go dance()
for{
;
}
}
- Goexit: 終止調用它的 go 程, 其它 go 程不會受影響
package main
import (
"fmt"
"runtime"
)
func main() {
go func() {
fmt.Println("123")
// 退出當前協程
//runtime.Goexit()
// 退出當前函數
//return
test()
fmt.Println("456")
}()
for{
;
}
}
func test() {
fmt.Println("abc")
// 只會結束當前函數, 協程中的其它代碼會繼續執行
//return
// 會結束整個協程, Goexit之後整個協程中的其它代碼不會執行
runtime.Goexit()
fmt.Println("def")
}
- NumCPU: 返回本地機器的邏輯 CPU 個數
package main
import (
"fmt"
"runtime"
)
func main() {
num := runtime.NumCPU()
fmt.Println(num)
}
-
GOMAXPROCS: 設置可同時執行的最大 CPU 數,並返回先前的設置
-
Go 語言 1.8 之前, 需要我們手動設置
-
Go 語言 1.8 之後, 不需要我們手動設置
func main() {
// 獲取帶來了CPU個數
num := runtime.NumCPU()
// 設置同時使用CPU個數
runtime.GOMAXPROCS(num)
}
聲明:本文爲原創
一個人走的太慢,一羣人才能走的更遠。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/47a26-HLSKxMM0hjl7SSAg