# Day14 Go併發症狀- Goroutines (go) ## 併發是什麼症狀 是併發運算,不是某種併發症。 就是多線程運算。 * 併發(Concurrency) 不是併行(Parallelism) 雖然兩者在口語表達上都會說成『同時進行』,但是中文的『同時』很難區分是併行還是併發 但從英文單字來看的話,會比較容易理解 `併發`是`共享時間運算`,在一段時間內輪流享有時間資源 `併行`是`平行運算`,一直都能享有時間資源 `併發`是把時間切成很小很小段,在這小段的時間裡先後執行多項任務。 `併行`是CPU有多個核心,可以同時處理多個任務。 簡單來說,拿人來比喻的話: `併發`:`一個人`在一段時間內做`兩件事` `併行`:`兩個人`同時在做事 ## Go併發(Goroutines) `Goroutine`是輕量級的線程。 > A goroutine is a lightweight thread managed by the Go runtime. 而`main func`則是程式當前的、主要的`goroutine`。 想要讓Go併發**並ㄅㄧㄤˋ叫**非常的容易, 只要寫好一個`func`,在呼叫時多一個`go`關鍵字即可。 多線程讓你的程式嚇嚇叫。 #### 【Go Func】 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- ```go func main() { go test() time.Sleep(time.Second * 1) } func test() { fmt.Println("嚇嚇叫") } /* result: 嚇嚇叫 */ ``` ## 【Go併發過程】 `GO的併發`會用到多個核心下去執行,試著執行以下的程式看看, 輸出是一段一段的,一下`O`一下`-`交錯著,代表兩邊的線程都很努力的想噴射把值Print出來。 https://play.golang.org/p/Y29gVHLFTUC ```go 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】 `runtime.GOMAXPROCS(n)` 這一參數限制程式執行時 CPU用到的最大核心數量。 如果設置小於1,等於沒設,預設值是電腦核心數。 > GOMAXPROCS sets the maximum number of CPUs that can be executing. > If `n < 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`提出去 取一個好名字 再呼叫名字使用。** 沒名字、無名氏的下場就像免洗餐具一樣,只能用一次。 等於是濃縮了以下幾行程式碼 ```go func main(){ test() } func test(){ ... } ``` #### 【匿名併發】 `匿名併發` = `匿名函式`加上`go併發` https://play.golang.org/p/yjlln9Th00a ```go 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` 跟 `退出程序`是什麼關係? 一群地鼠原本在地道挖坑,突然發生恐慌,每隻地鼠抗性不夠、都得了負面狀態, 嚇得趕快逃走,於是就退出程序了。 #### 【Panic】 `Panic` 是發生了預期之外的事情,導致異常、錯誤的產生,退出程序的同時回傳`錯誤代碼2 (Process finished with exit code 2)`。我們可以透過`panic`的func來主動引起錯誤發生。 要注意的是若在`併發線程`中發生了`panic`,也會導致主程式也異常結束。 ```go 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 */ ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up