introduction
- 類似unix中的管道(pipe),或是隊列(queue)
- channel為引用類型的數據結構
- 先進先出
- 線程安全,多個goroutine同時訪問,不需要加鎖
- channel是有類型的,整數的channel只能存放整數,依此類推
- 使用
make()
初始化管道
聲明
1 | var 變量名 chan 類型 |
- 通常變量名會希望知道其為管道,因此會將chan結合其他關鍵字作為變量名
1
2var IntChan chan int
var StrChan chan string
操作管道
example 1
1 | package main |
result
1 | 10 |
example 2
1 | package main |
- 若管道類型為
interface{}
則可以接受任何類型數據的寫入
result
1 | {Tom 18} |
tips
從channel讀取數據
1 | a = <- testChan |
- 賦值記得加上
=
- 聲明並賦值加上
:=
從channel寫入數據
1 | testChan <- a |
channel和goroutine相結合
使用goroutine搭配channel實現讀寫
example
1 | package main |
result
1 | 5 |
阻塞
- 若是存放進去管道的數據數量超過管道的容量又沒有其他協程取出數據,管道會一直阻塞等待數據被取出
1 | package main |
result
1 | input the data 5 |
- 從上述可看到寫入的協程馬上寫入了5組數據,但要再寫入時因為管道已經滿了,必須等待讀的協程取出數據,才能在寫入數據到管道中
檢測channel是否被關閉
- 使用
close()
函數關閉channel - 在關閉channel後,管道只可讀(取出)不可再寫入
- 在取數據時可判斷管道是否已關閉,將最後一個數據取出時便會停止再取出數據
- 安全的取出數據,才不會造成死循環
val,ok := <- channel
- 當
ok
的值返回false
時代表已從關閉的管道中取出最後一條數據
example
1 | package main |
- 要是沒有使用
ok
判斷已關閉的管道是否還有下一條數據時,for
循環會不斷的訪問管道造成死循環
result
1 | 0 |
channel之間的同步
- 不再使用
time.Sleep()
等待線程完成任務
example 1
- 查找1-100的所有質數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40package main
import "fmt"
func calc(i int,ResultNum chan int,Sync_signal chan bool){
//退出時傳一個訊號到同步的channel中表示此任務已完成
defer func(){
Sync_signal <- true
}()
//判斷質數
for j:=2;j<i;j++{
if i%j == 0 {
return
}
}
ResultNum <- i
return
}
func main(){
//建立接收同步訊號的channel(可以為任何類型的管道)
//只要最後傳入訊號即可
Sync := make(chan bool,100)
ResultNum := make(chan int,100)
for i:=1;i<=100;i++{
go calc(i,ResultNum,Sync)
}
//上面共開啟100個線程,因此只要能取出100個訊號則代表子線程皆完成任務
for i:=1;i<=100;i++{
//沒有變量接收訊號,代表將訊號取出後就直接丟棄
<- Sync
}
//主線程關閉管道
close(ResultNum)
//遍歷管道計算的結果
for v := range(ResultNum){
fmt.Println(v)
}
} - 使用
range()
遍歷管道時,請注意管道必須先關閉(close()
)否則panic
(Dead Lock)
result
1 | 2 |
example 2
1 | package main |
result
1 | Tripoli |
(補充)管道的只讀與只寫
通常不會用以下的方法直接創建,會直接拿來當參數的類型使用居多
- 於函數中接受的形式參數類型為 只讀(只寫) 的通道時,可傳入可讀寫的雙向通道作為實體參數 (推薦)
創建可讀可寫管道(一般)
1 | var 變量名 = make(chan int,1) |
創建只讀的管道
1 | var 變量名 <- chan int |
創建只寫的管道
1 | var 變量名 chan <- int |
對channel進行select操作
應用1
管道都有固定大小,
實際業務開發中,有可能會有管道空間不夠使用的情況,
此時會一直阻塞,並等待數據被取出,才能再次寫入數據,
若是一直無法將數據取出,會造成之後要寫入的數據無法進入管道,而使得業務停擺
解決方式:
1 | package main |
result
1 | 21 can't send to Channel |
應用2
管道可能為空的情況下,
有可能仍要持續的取出存放在管道內的數據,
否則會一直阻塞,等待管道中有數據被寫入,才能再次取出數據,
若是一直無數據被寫入,會造成業務停擺
解決方式:
1 | package main |
result
1 | data 1 is got |