是併發運算,不是某種併發症。
就是多線程運算。
雖然兩者在口語表達上都會說成『同時進行』,但是中文的『同時』很難區分是併行還是併發
但從英文單字來看的話,會比較容易理解
併發是共享時間運算,在一段時間內輪流享有時間資源
併行是平行運算,一直都能享有時間資源
併發是把時間切成很小很小段,在這小段的時間裡先後執行多項任務。
併行是CPU有多個核心,可以同時處理多個任務。
簡單來說,拿人來比喻的話:
併發:一個人在一段時間內做兩件事
併行:兩個人同時在做事
Goroutine是輕量級的線程。
A goroutine is a lightweight thread managed by the Go runtime.
而main func則是程式當前的、主要的goroutine。
想要讓Go併發並ㄅㄧㄤˋ叫非常的容易,
只要寫好一個func,在呼叫時多一個go關鍵字即可。
多線程讓你的程式嚇嚇叫。
https://py.golang.org/p/QQWHnuHJdkv
package main
import (
"fmt"
"time"
)
func main() {
go test()
}
func test() {
time.Sleep(time.Second * 1)
fmt.Println("嚇嚇叫")
}
/* result:
*/
疑?奇怪,怎麼沒有輸出,是我的Go死掉了嗎?
說好的嚇嚇叫呢?
非也。
主程式func main確實已經執行完畢了,
但是被你go出去的func還在time.Sleep,而且睡的很爽。
這可是一秒鐘耶,對電腦來講天文數字啊!
所以被你go出去的func睡飽回來發現main主線程已經早已結束返回,
自然看不見fmt.Print的輸出了。
稍微改一下,給主線程睡一會,這樣就可以了。
https://play.golang.org/p/Mn9klYHEmU-
func main() {
go test()
time.Sleep(time.Second * 1)
}
func test() {
fmt.Println("嚇嚇叫")
}
/* result:
嚇嚇叫
*/
GO的併發會用到多個核心下去執行,試著執行以下的程式看看,
輸出是一段一段的,一下O一下-交錯著,代表兩邊的線程都很努力的想噴射把值Print出來。
https://play.golang.org/p/Y29gVHLFTUC
func main() {
go print1()
go print2()
time.Sleep(time.Second)
}
func print1() {
for i := 0; i < 1000; i++ {
fmt.Print("O")
}
}
func print2() {
for i := 0; i < 1000; i++ {
fmt.Print("-")
}
}
/* result:
OOOOOOOOOOOOOOOOOOOOOOOOO--------OO-----------OOOOOOOOOOO...
*/
runtime.GOMAXPROCS(n) 這一參數限制程式執行時 CPU用到的最大核心數量。
如果設置小於1,等於沒設,預設值是電腦核心數。
GOMAXPROCS sets the maximum number of CPUs that can be executing.
Ifn < 1, it does not change the current setting.
https://play.golang.org/p/idAyE6AsCJ1
但限制了一核心之後,為什麼還是可以把兩個print func都印到呢,
怎麼不是只印出一個直到時間到?說好的單核心?
原來是Go Routine會去排程,執行A線程一小段時間後會跳到線程B去,
這才公平合理嘛!不然CPU資源都被其中一個線程給佔住,作業系統就卡死啦。
所以這次看到的輸出會是 O很多 再來-很多,兩者都連續印很多的情況下交錯著。
匿名函式(Anonymous Function)是什麼?
第一次看到這麼名字時不明覺厲,可能跟匿名者聯想到了一起吧,
但事實上用匿名函式並不是多厲害的事情,而是因為簡便,
因為我懶得再幫你這個func提出去 取一個好名字 再呼叫名字使用。
沒名字、無名氏的下場就像免洗餐具一樣,只能用一次。
等於是濃縮了以下幾行程式碼
func main(){
test()
}
func test(){
...
}
匿名併發 = 匿名函式加上go併發
https://play.golang.org/p/yjlln9Th00a
func main() {
go func() { // 把這裡的go併發去掉,就變成匿名函式了
for i := 0; i < 10000; i++ {
fmt.Print("匿名併發函式 ")
}
}() // 這邊有小括號有點奇怪對不? 因為他是一個函式,在呼叫時需要()
for i := 0; i < 10000; i++ {
fmt.Print("main ")
}
time.Sleep(10000)
}
你不一定每次都能期待被你併發出去的func順利執行完任務並且會傳值給你,
人家主線程main func早就打烊、結束營業啦,就算回傳了我也沒辦法接到。
所以說,被go出去的func 沒有return回傳值,
因為不能期待他每次都能成功回傳值、不能期待主線程每次都等他們完成。
那要回傳值給main func知道,該怎麼辦呢?
此時就需要自己建立通道(channel)了。
詳見明天的文章。
恐慌Panic 跟 退出程序是什麼關係?
一群地鼠原本在地道挖坑,突然發生恐慌,每隻地鼠抗性不夠、都得了負面狀態,
嚇得趕快逃走,於是就退出程序了。
Panic 是發生了預期之外的事情,導致異常、錯誤的產生,退出程序的同時回傳錯誤代碼2 (Process finished with exit code 2)。我們可以透過panic的func來主動引起錯誤發生。
要注意的是若在併發線程中發生了panic,也會導致主程式也異常結束。
func main() {
fmt.Println("程式開始")
go p()
time.Sleep(time.Second * 1)
fmt.Println("主程式順利結束")
}
func p() {
fmt.Println("即將發生空難...")
panic("空難")
}
/* result:
程式開始
即將發生空難...
panic: 空難
goroutine 18 [running]:
main.p()
/tmp/sandbox810050981/prog.go:18 +0x95
created by main.main
/tmp/sandbox810050981/prog.go:11 +0x91
*/