【更多流程控制】4. go語句初探

go語句初探

    go語句和通道類型是Go語言的併發編程理念的最終體現。在第五章,我已經詳細介紹過了通道類型。相比之下,go語句在用法上要比通道簡單不少。與defer語句相同,go語句也能夠攜帶一條表達式語句。注意,go語句的執行會很快結束,並不會對當前流程的進行形成阻塞或明顯的延遲。一個簡單的示例以下:編程

go fmt.Println("Go!")

    能夠看到,go語句僅由一個關鍵字go和一條表達式語句構成。一樣的,go語句的執行與其攜帶的表達式語句的執行在時間上沒有必然聯繫。這裏可以肯定的僅僅是後者會在前者完成以後發生。在go語句被執行時,其攜帶的函數(也被稱爲go函數)以及要傳給它的若干參數(若是有的話)會被封裝成一個實體(即Goroutine),並被放入到相應的待運行隊列中。Go語言的運行時系統會適時的從隊列中取出待運行的Goroutine並執行相應的函數調用操做。注意,對傳遞給這裏的函數的那些參數的求值會在go語句被執行時進行。這一點也是與defer語句相似的。
  
    正是因爲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毫秒。100毫秒雖短暫,但足夠go函數被調度執行的了。上述命令源碼文件在被運行時會如咱們所願地在標準輸出上打印出Go!spa

  
    另外一個比較紳士的作法是在main函數的最後調用runtime.Gosched函數。相應的程序版本以下:code

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的運行的調度。
  
    固然,咱們還有其它方法能夠知足上述需求。而且,若是咱們須要去左右更多的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類型有三個方法可用——AddDoneWaitAdd會使其所屬值的一個內置整數獲得相應增長,Done會使那個整數減1,而Wait方法會使當前Goroutine(這裏是運行main函數的那個Goroutine)阻塞直到那個整數爲0。這下你應該明白上面這個示例所採用的方法了。咱們在main函數中啓用了三個Goroutine來封裝三個go函數。每一個匿名函數的最後都調用了wg.Done方法,並以此表達當前的go函數會當即執行結束的狀況。當這三個go函數都調用過wg.Done函數以後,處於main函數最後的那條wg.Wait()語句的阻塞做用將會消失,main函數的執行將當即結束。
  
    與go語句、go函數以及承載其運行的Goroutine相關的話題其實還有不少。不過因爲篇幅等緣由,個人講述就先到此爲止。若是你們對這方面的內容感興趣的話請參看《Go併發編程實戰》這本書。這裏面會有很是詳盡的介紹。源碼

相關文章
相關標籤/搜索