Go 語言支持并發(fā),我們只需要通過 go 關(guān)鍵字來開啟 goroutine 即可搀军。
goroutine 是輕量級線程破托,goroutine 的調(diào)度是由 Golang 運行時進行管理的铝阐。
goroutine 語法格式:
go函數(shù)名( 參數(shù)列表 )
例如:
gof(x, y, z)
開啟一個新的 goroutine:
f(x, y, z)
Go 允許使用 go 語句開啟一個新的運行期線程屉更, 即 goroutine,以一個不同的、新創(chuàng)建的 goroutine 來執(zhí)行一個函數(shù)舍扰。 同一個程序中的所有 goroutine 共享同一個地址空間。
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() {
go say("world")
say("hello")
}
執(zhí)行以上代碼希坚,你會看到輸出的 hello 和 world 是沒有固定先后順序边苹。因為它們是兩個 goroutine 在執(zhí)行:
world
hello
hello
world
world
hello
hello
world
world
hello
通道(channel)是用來傳遞數(shù)據(jù)的一個數(shù)據(jù)結(jié)構(gòu)。
通道可用于兩個 goroutine 之間通過傳遞一個指定類型的值來同步運行和通訊裁僧。操作符?<-?用于指定通道的方向个束,發(fā)送或接收。如果未指定方向聊疲,則為雙向通道茬底。
ch <- v// 把 v 發(fā)送到通道 chv := <-ch// 從 ch 接收數(shù)據(jù)// 并把值賦給 v
聲明一個通道很簡單,我們使用chan關(guān)鍵字即可获洲,通道在使用前必須先創(chuàng)建:
ch :=make(chanint)
注意:默認情況下阱表,通道是不帶緩沖區(qū)的。發(fā)送端發(fā)送數(shù)據(jù)贡珊,同時必須有接收端相應(yīng)的接收數(shù)據(jù)最爬。
以下實例通過兩個 goroutine 來計算數(shù)字之和,在 goroutine 完成計算后门岔,它會計算兩個結(jié)果的和:
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 發(fā)送到通道 c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 從通道 c 中接收
fmt.Println(x, y, x+y)
}
輸出結(jié)果為:
-5 17 12
通道可以設(shè)置緩沖區(qū)爱致,通過 make 的第二個參數(shù)指定緩沖區(qū)大小:
ch :=make(chanint,100)
帶緩沖區(qū)的通道允許發(fā)送端的數(shù)據(jù)發(fā)送和接收端的數(shù)據(jù)獲取處于異步狀態(tài)寒随,就是說發(fā)送端發(fā)送的數(shù)據(jù)可以放在緩沖區(qū)里面糠悯,可以等待接收端去獲取數(shù)據(jù)帮坚,而不是立刻需要接收端去獲取數(shù)據(jù)。
不過由于緩沖區(qū)的大小是有限的互艾,所以還是必須有接收端來接收數(shù)據(jù)的试和,否則緩沖區(qū)一滿,數(shù)據(jù)發(fā)送端就無法再發(fā)送數(shù)據(jù)了忘朝。
注意:如果通道不帶緩沖灰署,發(fā)送方會阻塞直到接收方從通道中接收了值判帮。如果通道帶緩沖局嘁,發(fā)送方則會阻塞直到發(fā)送的值被拷貝到緩沖區(qū)內(nèi);如果緩沖區(qū)已滿晦墙,則意味著需要等待直到某個接收方獲取到一個值悦昵。接收方在有值可以接收之前會一直阻塞。
package main
import "fmt"
func main() {
// 這里我們定義了一個可以存儲整數(shù)類型的帶緩沖通道
// 緩沖區(qū)大小為2
ch := make(chan int, 2)
// 因為 ch 是帶緩沖的通道晌畅,我們可以同時發(fā)送兩個數(shù)據(jù)
// 而不用立刻需要去同步讀取數(shù)據(jù)
ch <- 1
ch <- 2
// 獲取這兩個數(shù)據(jù)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
執(zhí)行輸出結(jié)果為:
1
2
Go 通過 range 關(guān)鍵字來實現(xiàn)遍歷讀取到的數(shù)據(jù)但指,類似于與數(shù)組或切片。格式如下:
v, ok := <-ch
如果通道接收不到數(shù)據(jù)后 ok 就為 false抗楔,這時通道就可以使用 close() 函數(shù)來關(guān)閉棋凳。
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)
go fibonacci(cap(c), c)
// range 函數(shù)遍歷每個從通道接收到的數(shù)據(jù),因為 c 在發(fā)送完 10 個
// 數(shù)據(jù)之后就關(guān)閉了通道连躏,所以這里我們 range 函數(shù)在接收到 10 個數(shù)據(jù)
// 之后就結(jié)束了剩岳。如果上面的 c 通道不關(guān)閉,那么 range 函數(shù)就不
// 會結(jié)束入热,從而在接收第 11 個數(shù)據(jù)的時候就阻塞了拍棕。
for i := range c {
fmt.Println(i)
}
}
執(zhí)行輸出結(jié)果為:
0
1
1
2
3
5
8
13
21
34