• 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

    微信掃碼關注站長公衆號,和站長交流學習

    相關文章