## 一、定義
Go 語言支持并發,我們只需要通過 go 關鍵字來開啟 goroutine 即可。
goroutine 是輕量級線程,goroutine 的調度是由 Golang 運行時進行管理的。
goroutine 語法格式:
~~~
go 函數名( 參數列表 )
~~~
例如:
~~~
go f(x, y, z)
~~~
開啟一個新的 goroutine:
~~~
f(x, y, z)
~~~
Go 允許使用 go 語句開啟一個新的運行期線程, 即 goroutine,以一個不同的、新創建的 goroutine 來執行一個函數。 同一個程序中的所有 goroutine 共享同一個地址空間。
### 1、實例
~~~
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")
}
~~~
執行結果:
world
hello
hello
world
world
hello
hello
world
world
hello
注:輸出的 hello 和 world 是沒有固定先后順序
## 二、通道(channel)
通道(channel)是用來傳遞數據的一個數據結構。
通道可用于兩個 goroutine 之間通過傳遞一個指定類型的值來同步運行和通訊。操作符`<-`用于指定通道的方向,發送或接收。如果未指定方向,則為雙向通道。
~~~
ch <- v // 把 v 發送到通道 ch
v := <-ch // 從 ch 接收數據
// 并把值賦給 v
~~~
聲明一個通道很簡單,我們使用chan關鍵字即可,通道在使用前必須先創建:
~~~
ch := make(chan int)
~~~
**注意**:默認情況下,通道是不帶緩沖區的。發送端發送數據,同時必須有接收端相應的接收數據。
以下實例通過兩個 goroutine 來計算數字之和,在 goroutine 完成計算后,它會計算兩個結果的和:
## 1、實例
~~~
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 發送到通道 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)
}
~~~
執行結果:
-5 17 12
## 三、通道緩沖區
通道可以設置緩沖區,通過 make 的第二個參數指定緩沖區大小:
~~~
ch := make(chan int, 100)
~~~
帶緩沖區的通道允許發送端的數據發送和接收端的數據獲取處于異步狀態,就是說發送端發送的數據可以放在緩沖區里面,可以等待接收端去獲取數據,而不是立刻需要接收端去獲取數據。
不過由于緩沖區的大小是有限的,所以還是必須有接收端來接收數據的,否則緩沖區一滿,數據發送端就無法再發送數據了。
**注意**:如果通道不帶緩沖,發送方會阻塞直到接收方從通道中接收了值。如果通道帶緩沖,發送方則會阻塞直到發送的值被拷貝到緩沖區內;如果緩沖區已滿,則意味著需要等待直到某個接收方獲取到一個值。接收方在有值可以接收之前會一直阻塞。
### 1、實例
~~~
package main
import "fmt"
func main() {
// 這里我們定義了一個可以存儲整數類型的帶緩沖通道
// 緩沖區大小為2
ch := make(chan int, 2)
// 因為 ch 是帶緩沖的通道,我們可以同時發送兩個數據
// 而不用立刻需要去同步讀取數據
ch <- 10
ch <- 20
// 獲取這兩個數據
fmt.Println(<-ch)
fmt.Println(<-ch)
}
~~~
執行結果:
10
20
## 四、Go 遍歷通道與關閉通道
Go 通過 range 關鍵字來實現遍歷讀取到的數據,類似于與數組或切片。格式如下:
~~~
v, ok := <-ch
~~~
如果通道接收不到數據后 ok 就為 false,這時通道就可以使用close()函數來關閉。
### 1、實例
~~~
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 函數遍歷每個從通道接收到的數據,因為 c 在發送完 10 個
// 數據之后就關閉了通道,所以這里我們 range 函數在接收到 10 個數據
// 之后就結束了。如果上面的 c 通道不關閉,那么 range 函數就不
// 會結束,從而在接收第 11 個數據的時候就阻塞了。
for i := range c {
fmt.Println(i)
}
}
~~~
執行結果:
0
1
1
2
3
5
8
13
21
34