Golang學習摘錄(二)

Golang學習二

  1. Full slice expressions切片操作

    • Python a[low: high: direction]

      比如 a = [1, 2, 3], a[1:2:-1]負1表示反方向,結果是[3,2]

    • Golang第三個不是表示方向:a[low : high : max],string類型切片不支持max操作。

      max參數用來指定返回的切片的容量,slice在golang裏底層是數組,並且有默認初始大小cap(a)。指定max參數後,返回的slice底層數組將不再是默認大小,而是max-low大小.

      所以有如下約定: 0 <= low <= high <= max <= cap(a)

      a := [5]int{1, 2, 3, 4, 5}

      t := a[1:3:9]

      此時切片t類型是[]int,長度length是2,容量是9-1等於8.意思是說,t底層數組長度爲8,後續對t做append操作,只要大小不超過8,不必重新分配地址。

  2. Type assertions

    go的類型判斷有點特別,v, ok := x.(T)斷言x不是nil並且x類型是T。如果類型判斷正確,ok爲true,v爲x轉化爲T後的值。如果失敗,ok爲false,v是T類型的zero_value.

  3. 協程入門

    如果有Python功底,對協程coroutine很熟悉,理解goroutine分分鐘的事。goroutine與coroutine兩個單詞長這麼像,就是因爲本來就一回事。不理解也沒事,先看看協程。

    greenlet:python協程基礎框架,很原始,是純手工的協程框架

    • from greenlet import greenlet
      def test1():
          print 12
          gr2.switch()
          print 34
      def test2():
          print 56
          gr1.switch()
          print 78
      gr1 = greenlet(test1)
      gr2 = greenlet(test2)#創建兩個協程test1,test2,但並未啓動
      gr1.switch()#切換到gr1,即啓動協程gr1
      

    創建兩個協程(greenlet框架叫做greenlet),分別是test1,test2,但是並未啓動兩個協程。然後主協程執行gr1.switch(),CPU執行權交給協程test1,輸出12,然後切換到協程test2輸出56,然後CPU執行權又切回test1,輸出34。由於此時並沒有切換到test2,78不會被輸出,程序結束。

    上面就是協程入門,可以看到greenlet所有切換工作必須由代碼顯示執行,框架不會自動調度,所以說greenlet是純手工的框架。協程跟多線程有個最大區別:某個協程如果不通過switch交出CPU使用權,其他協程無法獲得CPU。在一個線程內部,包含着很多協程,當該線程獲得CPU時,該線程內的switch進來(獲得CPU)但是還沒有switch出去(交出CPU)的協程獲得CPU,其他協程只能等待它switch交出CPU使用權。

    gevent是比greenlet高級的協程框架,前面說了,greenlet的協程切換時純手工,原始,那麼gevent就是高級的:gevent會自動創建一個主協程,級別比較高,我們叫做調度器,它會自動調度該線程內的很多其他協程,當某個協程阻塞住,強行要回CPU使用權,給其他沒有阻塞住的協程使用,如果所有其他的協程都阻塞,它就自個拿着CPU使用權,輪詢其他協程,看誰不阻塞了,再交給誰。gevent能讓程序員像寫多線程一樣寫多協程,切換,調度,框架都做好了,並且接口與多線程類似,學習難度低。

  4. goroutine

    前面說了協程,golang的goroutine庫就是更高級的gevent,也是自動調度,想要goroutine同步就要使用channel或者其他互斥元,與多線程編程十分類似。

    unbuffered channel可以用來同步goroutine

    buffered channel可以用作counting semaphore,可以參考上一篇golang學習文章裏的信號量限流的例子

    sync包裏提供了sync.Mutex or sync.RWMutex兩種互斥元。說直白點就是鎖,獲取鎖,釋放鎖從而同步

    sync.Once用來保證一堆goroutine執行同一個函數,只會執行一次,其他的阻塞直到那一次執行結束

    var a string
    var once sync.Once
    func setup() {
        a = "hello, world"
    }
    func doprint() {
        once.Do(setup)
        print(a)
    }
    func threeprint() {
        go doprint()
        go doprint()
        go doprint()
    }
    

    雖然起了3個goroutine去執行setup,但是once.Do保證只會有一個goroutine執行一次setup,其他goroutine等待它setup結束,然後打印3次。

  5. 注意閉包的調用

    在循環中調用函數或者goroutine方法,一定要採用顯示的變量調用,不要在閉包函數裏面使用循環的變量

    for i:=0; i<limit; i++{
        go func(){ DoSomething(i) }() //錯誤的做法
        go func(i int){ DoSomething(i) }(i)//正確的做法
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章