這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
10.蛤蟆筆記go語言——並發
goroutine
goroutine 是由 Go 運行時環境管理的輕量級線程。
go f(x, y, z)
開啟一個新的 goroutine 執行
f(x, y, z)
f , x , y 和 z 是當前 goroutine中定義的,但是在新的 goroutine 中運行 `f`。
goroutine 在相同的地址空間中運行,因此訪問共用記憶體必須進行同步。sync 提供了這種可能,不過在Go 中並不經常用到,因為有其他的辦法。
package main
import (
"fmt"
"time"
)
func say(s string) {
for i:= 0; i < 5; i++ {
time.Sleep(100* time.Millisecond)
fmt.Println(s)
}
}
func main() {
gosay("world")
say("hello")
}
channel
channel 是有類型的管道,可以用 channel 操作符 <- 對其發送或者接收值。
ch <- v // 將 v 送入 channel ch。
v := <-ch // 從 ch 接收,並且賦值給 v。
(“箭頭”就是資料流的方向。)
和 map 與 slice 一樣,channel 使用前必須建立:
ch := make(chan int)
預設情況下,在另一端準備好之前,發送和接收都會阻塞。這使得 goroutine 可以在沒有明確的鎖或競態變數的情況下進行同步。
package main
import "fmt"
func sum(a []int, c chan int) {
sum :=0
for _,v := range a {
sum+= v
}
c <-sum //將和送入c
}
func main() {
a :=[]int{7, 2, 8, -9, 4, 0}
c :=make(chan int)
gosum(a[:len(a)/2], c)
gosum(a[len(a)/2:], c)
x, y :=<-c, <-c //從c中擷取
fmt.Println(x,y, x+y)
}
執行:
-517 12
緩衝 channel
channel 可以是 _帶緩衝的_。為 make 提供第二個參數作為緩衝長度來初始化一個緩衝 channel:
ch := make(chan int, 100)
向緩衝 channel 發送資料的時候,只有在緩衝區滿的時候才會阻塞。當緩衝區清空的時候接受阻塞。
修改例子使得緩衝區被填滿,然後看看會發生什麼。
package main
import "fmt"
func main() {
c :=make(chan int, 2)
c <-1
c <-2
fmt.Println(<-c)
fmt.Println(<-c)
}
執行:
1
2
range 和 close
寄件者可以 close 一個 channel 來表示再沒有值會被發送了。接收者可以通過指派陳述式的第二參數來測試 channel 是否被關閉:當沒有值可以接收並且 channel 已經被關閉,那麼經過v, ok := <-ch
之後 ok 會被設定為 `false`。
迴圈 `for i := range c` 會不斷從 channel 接收值,直到它被關閉。
注意: 只有寄件者才能關閉 channel,而不是接收者。向一個已經關閉的 channel 發送資料會引起 panic。 還要注意: channel 與檔案不同;通常情況下無需關閉它們。只有在需要告訴接收者沒有更多的資料的時候才有必要進行關閉,例如中斷一個 `range`。
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y :=0, 1
for i:= 0; i < n; i++ {
c<- x
x,y = y, x+y
}
close(c)
}
func main() {
c :=make(chan int, 10)
gofibonacci(cap(c), c)
for i:= range c {
fmt.Println(i)
}
}
執行:
0
1
1
2
3
5
8
13
21
34
select
select 語句使得一個 goroutine 在多個通訊操作上等待。
select 會阻塞,直到條件分支中的某個可以繼續執行,這時就會執行那個條件分支。當多個都準備好的時候,會隨機播放一個。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y :=0, 1
for {
select{
casec <- x:
x,y = y, x+y
case<-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c :=make(chan int)
quit :=make(chan int)
gofunc() {
fori := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit<- 0
}()
fibonacci(c,quit)
}
執行:
0
1
1
2
3
5
8
13
21
34
quit
預設選擇
當 select 中的其他條件分支都沒有準備好的時候,`default` 分支會被執行。
為了非阻塞的發送或者接收,可使用 default 分支:
select {
case i := <-c:
// 使用 i
default:
// 從 c 讀取會阻塞
}
package main
import (
"fmt"
"time"
)
func main() {
tick :=time.Tick(100 * time.Millisecond)
boom :=time.After(500 * time.Millisecond)
for {
select{
case<-tick:
fmt.Println("tick.")
case<-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50* time.Millisecond)
}
}
}
執行:
.
.
tick.
.
.
tick.
.
.
tick.
.
.
tick.
.
.
BOOM!
入門結束
Go文檔 是一個極好的 開始。 它包含了參考、指南、視頻等等更多資料。
瞭解如何組織 Go 代碼並在其上工作,參閱 這個視頻,或者閱讀 如何編寫 Go 代碼。
在標準庫上需要協助的話,參考 包手冊。語言本身的協助,閱讀 語言規範是件令人愉快的事情。
進一步探索 Go 的並行存取模型,參閱 Go並行存取模型 (投影片) 以及 深入 Go 並行存取模型 (投影片) 並且閱讀 使用通訊共用記憶體 的代碼之旅。
想要開始編寫 Web 應用程式,參閱 一個簡單的編程環境 (投影片) 並且閱讀 編寫 Web應用 的指南.
GO中的一等公民函數 展示了有趣的函數類型。
Go Blog 有著眾多的關於 Go 的文章資訊。
mikespook的部落格有大量中文的關於 Go 的文章和翻譯。
開源電子書 GoWeb編程 和 Go入門指南 能夠協助你更加深入的瞭解和學習 Go 語言。
訪問 golang.org 瞭解更多內容。
關於本項目(中文)的任何意見、建議,請在這裡提交 Issues。