Golang 筆記 5 go語句

   Go語句和通道類型是Go語言的併發編程理念的最終體現。與defer語句相同,go語句也可以攜帶一個表達式語句。Go語句的執行會很快結束,並不會對當前流程的進行造成阻塞或明顯的延遲。一個簡單的示例:

go fmt.Println("Go")

  go語句僅由一個go關鍵字和一條表達式語句組成。同樣的,go語句的執行與其攜帶的表達式語句的執行在時間上沒有必然聯繫。這裏能確定的僅僅是後者會在前者完成之後發生。在go語句被執行時,其攜帶的函數(也稱爲go函數)以及要傳給它的若干參數會被封裝成一個實體(即Goroutine),並被放入到相應的待運行隊列中。Go語言的運行時系統會適時的從隊列中取出待運行的Goroutine並執行相應的函數調用操作。注意,對傳遞給這裏的函數的那些參數的求值會在go語句被執行時進行。
  正是由於go函數的執行時間的不確定性。所以Go提供了很多方法來協調它們的運行。其中最簡單粗暴的方法就是time.Sleep函數:

package main

import {
    "fmt"
}

func main() {
    go fmt.Println("Go!")
}

  上面的代碼運行時,不會有任何輸出,因爲還沒等go語言運行時系統調用那個go函數執行主函數main就已經執行完畢了。函數main的執行完畢意味着整個程序的執行的結束。因此,這個go函數就不會執行。
  當我們在上述go語句的後面添加一條對time.Sleep函數的調用語句之後情況就不同了:

package main

import {
    "fmt"
    "time"
}

func main() {
    go fmt.Println("Go!")
    time.Sleep(100 * time.Millisecond)
}

  語句time.Sleep(100 * time.Millisecond)會把main函數的執行結束時間向後延遲100毫秒,這樣go函數就會被調度執行了。
  另外比較優雅的做法是在main函數的最後調用runtime.Gosched函數:

package main

import {
    "fmt"
    "runtime"
}

func main() {
    go fmt.Println("Go")
    runtime.Gosched()
}

  runtime.Gosched函數的作用是讓當前正在運行的Goroutine(這裏是運行main函數的那個Goroutine)暫時“休息”一下,而讓Go運行時系統轉去運行其他的Goroutine(這裏是與go fmt.Println("Go!"))對應並會封裝fmt.Println("Go!")的那個Goroutine)。
  還有另外的方法可以滿足上述需求。如果我們要控制更多的Goroutine的運行時機的話,下面的方法更好:

package main

import {
    "fmt"
    "sync"
}

func main() {
    var wg sync.WaitGroup
    wg.Add(3)
    go func () {
        fmt.Println("Go!")
        wg.Done()
    }()
    
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    
    wg.Wait()
}

  sync.WaitGroup類型有三個方法可用--Add、Done和Wait。Add會使其所屬值的一個內置整數得到相應增加,Done會使那個整數減1,而wait方法會使當前Goroutine阻塞直到那個整數爲0。上例中,我們在main函數中啓用了三個Goroutine來封裝三個Go函數。每個匿名函數的最後都調用了wg.Done方法。並以此表達當前的go函數會立即執行結束的情況。當這三個go函數都調用過wg.Done函數之後,處於main函數最後的那條wg.Wait()語句就不會阻塞,main函數的執行將立即結束。

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