并發(fā)編程基本概念
- 學(xué)習(xí)并發(fā)編程之前我們需要腦補(bǔ)幾個(gè)基礎(chǔ)知識和思考一個(gè)問題
- 什么是串行?
- 什么是并行?
- 什么是并發(fā)?
- 什么是程序?
- 什么是進(jìn)程?
- 什么是線程?
- 什么是協(xié)程?
-
什么是串行?
- 串行就是按順序執(zhí)行, 就好比銀行只有1個(gè)窗口, 有3個(gè)人要辦事, 那么必須排隊(duì), 只有前面的人辦完走人, 才能輪到你
- 在計(jì)算機(jī)中, 同一時(shí)刻, 只能有一條指令, 在一個(gè)CPU上執(zhí)行, 后面的指令必須等到前面指令執(zhí)行完才能執(zhí)行, 就是串行
-
什么是并行?
- 并行就是同時(shí)執(zhí)行, 就好比銀行有3個(gè)窗口, 有3個(gè)人要辦事, 只需要到空窗口即可立即辦事.
- 在計(jì)算機(jī)中, 同一時(shí)刻, 有多條指令, 在多個(gè)CPU上執(zhí)行, 就是并行
- 從以上分析不難看出, 并行的速度優(yōu)于串行
-
什么是并發(fā)?
- 并發(fā)是偽并行, 就好比銀行只有1個(gè)窗口, 有3個(gè)人要辦事, 那么沒輪到后面的人時(shí), 后面的人可以用拖鞋先排隊(duì), 去吃個(gè)早餐,買個(gè)東西啥的, 感覺差不多要到自己時(shí)再回來辦事
- 在計(jì)算機(jī)中, 同一時(shí)刻, 只能有一條指令, 在一個(gè)CPU上執(zhí)行, 但是CPU會快速的在多條指令之間輪詢執(zhí)行就是并發(fā)
- 并行和并發(fā)的區(qū)別就好比古代的三妻四妾(名正言順, 光明正大)和現(xiàn)代三妻四妾(抽空幽會, 小三小四)
-
總結(jié):
- 多線程程序在單核上運(yùn)行, 就是并發(fā)
- 多線程程序在多核上運(yùn)行,就是并行
- 什么是程序?
-
程序
是指編譯之后存儲在磁盤上的一個(gè)二進(jìn)制文件
, 會占用磁盤空間, 但不會占用系統(tǒng)資源
-
- 什么是進(jìn)程?
-
進(jìn)程
是指程序
在操作系統(tǒng)中的一次執(zhí)行過程, 是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位 - 例如:
- 啟動(dòng)記事本這個(gè)程序, 在系統(tǒng)中就會創(chuàng)建一個(gè)記事本進(jìn)程
- 再次啟動(dòng)記事本這個(gè)程序, 又會在系統(tǒng)中創(chuàng)建一個(gè)記事本進(jìn)程
- 程序和進(jìn)程的關(guān)系就好比劇本和演出的關(guān)系
- 劇本對應(yīng)程序, 演出對應(yīng)進(jìn)程. 同一個(gè)劇本可以在多個(gè)舞臺同時(shí)演出互不影響, 同一個(gè)程序可以在系統(tǒng)中開啟多個(gè)進(jìn)程互不影響
- 所以程序和進(jìn)程的關(guān)系是1:N, 所以多個(gè)進(jìn)程的空間是獨(dú)立的
-
- 什么是線程?
- 線程是指進(jìn)程中的一個(gè)執(zhí)行實(shí)例, 是程序執(zhí)行的最小單元, 它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位
- 一個(gè)進(jìn)程中至少有一個(gè)線程, 這個(gè)線程我們稱之為
主線程
- 一個(gè)進(jìn)程中除了
主線程
以外, 我們還可以創(chuàng)建和銷毀多個(gè)線程 - 例如:
- 啟動(dòng)迅雷這個(gè)程序, 系統(tǒng)會創(chuàng)建一個(gè)
迅雷進(jìn)程
, 并且默認(rèn)會有一個(gè)主線程
, 用于執(zhí)行迅雷默認(rèn)的業(yè)務(wù)邏輯 - 當(dāng)我們利用迅雷下載
多個(gè)任務(wù)
的時(shí)候, 會發(fā)現(xiàn)多個(gè)任務(wù)都在同時(shí)下載
, 此時(shí)為了能夠同時(shí)執(zhí)行
下載操作, 迅雷就會創(chuàng)建多個(gè)線程, 將不同的下載任務(wù)放到不同的線程中執(zhí)行
- 啟動(dòng)迅雷這個(gè)程序, 系統(tǒng)會創(chuàng)建一個(gè)
- 什么是協(xié)程?
- 協(xié)程是一種用戶態(tài)的輕量級線程币旧,又稱微線程甫贯,英文名Coroutine
- 與傳統(tǒng)的系統(tǒng)級別進(jìn)程和線程相比, 協(xié)程最大的優(yōu)勢在于"輕量級". 可以輕松創(chuàng)建上萬個(gè)不會導(dǎo)致系統(tǒng)資源衰竭. 而線程和進(jìn)程通常很難超過1萬個(gè).這也是協(xié)程稱之為"輕量級線程"的原因
- 一個(gè)線程中可以有任意多個(gè)協(xié)程, 但
某一時(shí)刻只能有一個(gè)協(xié)程在運(yùn)行
, 多個(gè)協(xié)程分享所在線程分配到的計(jì)算機(jī)資源 - 在協(xié)程中, 調(diào)用一個(gè)任務(wù)就像調(diào)用一個(gè)函數(shù)一樣, 消耗系統(tǒng)資源極少, 但能達(dá)到進(jìn)程、線程相同的并發(fā)效果
Go并發(fā)
Go在語言級別支持
協(xié)程
(多數(shù)語言在語法層面并不直接支持協(xié)程), 叫做goroutine.人們把Go語言稱之為21世紀(jì)的C語言. 第一是因?yàn)镚o語言設(shè)計(jì)簡單, 第二是因?yàn)?1世紀(jì)最重要的就是并行程序設(shè)計(jì).而Go從語言層面就支持并發(fā)和并行
Go并發(fā)小案例
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()
// 并行: 可以邊唱歌, 邊跳舞
// 注意點(diǎn): 主線程不能死, 否則程序就退出了
go sing() // 開啟一個(gè)協(xié)程
go dance() // 開啟一個(gè)協(xié)程
for{
;
}
}
- runtime包中常用的函數(shù)
- Gosched:使當(dāng)前go程放棄處理器荒叼,以讓其它go程運(yùn)行
package main import ( "fmt" "runtime" ) func sing() { for i:=0; i< 10; i++{ fmt.Println("我在唱歌") // Gosched使當(dāng)前go程放棄處理器染厅,以讓其它go程運(yùn)行泽谨。 // 它不會掛起當(dāng)前go程兔毒,因此當(dāng)前go程未來會恢復(fù)執(zhí)行 runtime.Gosched() } } func dance() { for i:=0; i< 10; i++{ fmt.Println("我在跳舞---") runtime.Gosched() } } func main() { go sing() go dance() for{ ; } }
- Goexit: 終止調(diào)用它的go程, 其它go程不會受影響
package main import ( "fmt" "runtime" ) func main() { go func() { fmt.Println("123") // 退出當(dāng)前協(xié)程 //runtime.Goexit() // 退出當(dāng)前函數(shù) //return test() fmt.Println("456") }() for{ ; } } func test() { fmt.Println("abc") // 只會結(jié)束當(dāng)前函數(shù), 協(xié)程中的其它代碼會繼續(xù)執(zhí)行 //return // 會結(jié)束整個(gè)協(xié)程, Goexit之后整個(gè)協(xié)程中的其它代碼不會執(zhí)行 runtime.Goexit() fmt.Println("def") }
- NumCPU: 返回本地機(jī)器的邏輯CPU個(gè)數(shù)
package main import ( "fmt" "runtime" ) func main() { num := runtime.NumCPU() fmt.Println(num) }
- GOMAXPROCS: 設(shè)置可同時(shí)執(zhí)行的最大CPU數(shù)咖楣,并返回先前的設(shè)置
- Go語言1.8之前, 需要我們手動(dòng)設(shè)置
- Go語言1.8之后, 不需要我們手動(dòng)設(shè)置
func main() { // 獲取帶來了CPU個(gè)數(shù) num := runtime.NumCPU() // 設(shè)置同時(shí)使用CPU個(gè)數(shù) runtime.GOMAXPROCS(num) }