+ goroutine 看一個需求
* 需求:要求統計 1 - 90000000000 的數字中,那些是素數?
+ 分析思路:
* 傳統的方法,就是使用一個循環,循環的判斷各個數是否是素數「效率很低」
* 使用併發或者並行的方式,將統計素數的任務分配個多個goroutine去完成,這時就會使用到goroutine「速度提升不少」
+ goroutine 基本介紹
> 進程和線程介紹
* 進程就是程序程序在操做系統中的一次執行過程,是系統進行資源分配和調度的基本單元
* 線程是進程的一個執行實例,是程序執行的最小單元,它是比進程更小的能獨立運行的基本單元
* 一個進程能夠建立和銷燬多個線程,同一個進程中的多個線程能夠併發執行
* 一個程序至少有一個進程,一個進程至少有一個線程
+ 併發和並行
* 多線程程序在單核上運行,就是併發
* 多線程程序在多核上運行,就是並行
+ 小結
* 併發: 由於是在一個cpu上, 好比有10個線程,每一個線程執行10ms(進行輪詢操做),從人的角度看,好像這10個線程都在運行,可是從微觀上看,
在某個時間點看,其實只有一個線程在執行,這就是併發。
* 並行: 由於是在多個cpu上(好比有10個cpu),好比有10個線程,每一個線程執行10ms(各自在不一樣的cpu上執行),從人的角度看,這10個線程都在運行,可是從微觀上看,在某個時間點看,也同時有10個線程在執行,這就是並行
+ go 協程和go主線程
* go主線程(有程序員直接稱爲線程/也能夠理解程進程) : 一個go線程上,能夠起多個協程,你能夠這裏理解,協程是輕量級的線程「編譯器作優化」
+ go 協程的特色
* 有獨立的棧空間
* 共享程序堆空間
* 調度由用戶控制
* 協程是輕量級的線程
+ channel (管道)-看個需求
* 需求: 如今要計算1-200 的各個數的階乘,而且把各個數的階乘放入到map中,最後顯示出來,要求使用goroutine完成
> 分析思路
* 使用goroutine來完成,效率高,可是會出現併發/並行安全問題
* 這裏就提出了不一樣的goroutine如何通訊的問題
> 代碼實現
* 使用goroutine 來完成(看看使用goroutine併發完成會出現什麼問題? 而後咱們去解決)
* 在運行某個程序時,如何知道是否存在資源競爭問題,方法很簡單,在編譯程序時。增長一個參數。 -race便可
* 代碼實現見 goroutine 和 channel/goroutine_channel.go
> 上面的案例就出現了資源的競爭的問題
+ 不一樣goroutine之間如何通信
* 全局變量的互斥鎖
* 使用管道channel來解決
+ 使用全局變量加鎖同步改進程序
* 由於沒有對全局變量m加鎖,所以會出現資源爭奪問題,代碼會出現錯誤,提示 fatal error: concurrent map writes
* 解決方案: 加入互斥鎖
* 咱們的數的階乘很大,結果會越界,能夠將求階乘改爲sum+=uint64(i)
* 改進代碼實現見 goroutine 和 channel/goroutine_channel_new.go
+ 爲何須要channel
* 前面使用全局變量加鎖同步來解決goroutine 的通信,但不完美
* 主線程在等待全部goroutine所有完成的時間很難肯定,咱們這裏設置10s,僅僅是估算
* 若是主線程休眠時間長了,會加長等待時間,若是時間短了,可能還有goroutine處於工做狀態,這時也會隨主線程的退出而銷燬
* 同步全局變量加鎖同步來通信,也不利用多個協程對全局變量的讀寫操做
* 上面種種分析都在呼喚一個新的通信機制- channel
+ channel 的基本介紹
* channel本質就是一個數據結構-隊列
* 數據是先進先出(FIFO: first in first out)
* 線程安全,多goroutine訪問時,不須要加鎖,就是說channel自己就是線程安全的
* channel有類型的,一個string的channel只能存放string類型數據
* channel 是線程安全的,多個協程操做同一個管道時,不會發生資源競爭問題
+ 定義/聲明channel
* var 變量名 chan 數據類型
* 舉例:
var intChan chan int (intChan 用戶存放int數據)
var mapChan chan map[int]string (mapChan 用戶存放map[int]string類型)
var perChan chan Person
var PerChan2 chan *Person
....
> 說明
- channel 是引用類型
- channel必須初始化才能寫入數據,即make後才能使用
- 管道是有類型的,intChan只能寫入整型int
+ 管道的初始化,寫入數據到管道,從管道讀取數據以及基本注意事項
* 代碼實現見 goroutine 和 channel/channel.go
+ channel 使用的注意事項
* channel 中只能存放指定的數據類型
* channel 的數據放滿後,就不能再放入了
* 若是從channel取出數據後,能夠繼續放入
* 在沒有使用協程的狀況下,若是channel數據取完了,在取,就會報dead lock
+ 讀寫channel 案例演示
* 案例見 goroutine 和 channel/intChan.go
* 案例見 goroutine 和 channel/mapChan.go
* 案例見 goroutine 和 channel/catChan.go
* 案例見 goroutine 和 channel/catChan2.go
- channel 的遍歷和關閉
+ channel的關閉
* 使用內置函數close 能夠關閉channel,當channel關閉後,就不能再向channel寫數據了,可是仍然能夠從該channel讀取數據
* 案例演示見goroutine 和 channel/close.go
- channel 的遍歷
+ channel支持for-range的方式進行遍歷,請注意兩個細節
* 再遍歷時,若是channel沒有關閉,則會出現deadlock的錯誤。
* 再遍歷時。若是channel已經關閉,則會正常遍歷數據,遍歷完後,就會退出遍歷
- channel遍歷和關閉的案例演示
+ 代碼演示 goroutine 和 channel/channel_for_close.go
- 應用實例
+ 請完成gorouteine 和 channel協同工做的案例。具體要求
* 開啓一個writeData協同,向管道intChan中寫入50個整數
* 開啓一個readData協程,從管道intChan中讀取writeData寫入的數據
* 注意: writeData 和 readData操做的是同一個管道
* 主線程須要等待writeData 和 readData協程都完成工做才能退出[管道]
* 案例代碼 goroutine 和 channel/gorouteine.go
+ channel 使用細節和注意事項
* channel能夠聲明爲只讀,或者只協性質
案例 chan01.go
* channel 只讀和只寫的最佳實踐案例
案例 chan02.go
* 使用select能夠解決從管道取數據的阻塞問題
* goroutine 中使用recover,解決協程中出現panic,致使程序崩潰問題
* 若是咱們起一個協程,可是這個協程出現了panic,若是咱們沒有捕獲這個panic,就會形成整個程序崩潰,這時咱們
能夠再goroutine中使用recover來捕獲panic,進行處理,這樣即便這個協程發生的問題,可是主線程仍然不受影響,能夠
繼續執行
.....詳細見下面鏈接
細說 goroutine 和 channelgit