Goroutines和Channels(一)
Go語言中的並發程序可以用兩種手段來實現。本章講解goroutine和channel,其支持“順序通信進程”(communicating sequential processes)或被簡稱為CSP。CSP是一種現代的並發編程模型,在這種編程模型中值會在不同的運行實例(goroutine)中傳遞,盡管大多數情況下仍然是被限制在單一實例中。
在Go語言中,每一個並發的執行單元叫作一個goroutine。設想這裏的一個程序有兩個函數,一個函數做計算,另一個輸出結果,假設兩個函數沒有相互之間的調用關系。
a.一個線性的程序會先調用其中的一個函數,然後再調用另一個。
b.如果程序中包含多個goroutine,對兩個函數的調用則可能發生在同一時刻。
如果你使用過操作系統或者其它語言提供的線程,那麽你可以簡單地把goroutine類比作一個線程,這樣你就可以寫出一些正確的程序了。
當一個程序啟動時,其主函數即在一個單獨的goroutine中運行,我們叫它main goroutine。
新的goroutine會用go語句來創建。在語法上,是一個普通的函數或方法調用前加上關鍵字go。
go語句會使其語句中的函數在一個新創建的goroutine中運行。而go語句本身會迅速地完成(意思就是創建goroutine的語句會立即執行,但是這個函數的執行時刻並不確定)。
示例:
f() // call f(); wait for it to return
go f() // create a new goroutine that calls f(); don‘t wait
下面的例子,main goroutine將計算菲波那契數列的第45個元素值。
由於計算函數使用低效的遞歸,所以會運行相當長時間,在此期間我們想讓用戶看到一個可見的標識來表明程序依然在正常運行,所以來做一個標記的打印:
package main import "fmt" import "time" func main() { go spinner(100 * time.Millisecond) const n = 45 fibN := fib(n) // slow fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN) } func spinner(delay time.Duration) { for { for _, r := range `-\|/` { fmt.Printf("\r%c", r) time.Sleep(delay) } } } func fib(x int) int { if x < 2 { return x } return fib(x-1) + fib(x-2) }
主函數返回時,所有的goroutine都會被直接打斷,程序退出。除了從主函數退出或者直接終止程序之外,沒有其它的編程方法能夠讓一個goroutine來打斷另一個的執行,但是之後可以看到一種方式來實現這個目的,通過goroutine之間的通信來讓一個goroutine請求其它的goroutine,並讓被請求的goroutine自行結束執行。
留意一下這裏的兩個獨立的單元是如何進行組合的,spinning和菲波那契的計算。分別在獨立的函數中,但兩個函數會同時執行。
Goroutines和Channels(一)