[Golang] goroutine & channel
Channel
Channel 是 Go 語言中用於 goroutine 之間通信的管道,是 Go 並發編程模型的核心組件之一。
基本概念
Channel 提供了一種 goroutine 之間的通信機制,使一個 goroutine 可以向另一個 goroutine 發送數據,而無需顯式的鎖或條件變量。
類型與創建
// 創建不同類型的 channel
ch1 := make(chan int) // 無緩衝的整數 channel
ch2 := make(chan string, 10) // 有 10 個緩衝區的字符串 channel
ch3 := make(chan interface{}) // 任意類型的 channel
發送與接收
// 發送數據
ch <- value
// 接收數據
value := <-ch
value, ok := <-ch // ok 表示 channel 是否已關閉
goroutine + Channel 範例:
package main
import "fmt"
import "time"
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan string, 1)
ch3 := make(chan interface{}, 1)
ch1 <- 1 // ch1 緩衝區已滿
// 創建 goroutine 來接收第一個值
go func() {
time.Sleep(2 * time.Second) // 等待1秒
fmt.Println(<-ch1) // 1,此時釋放 ch1 的空間
}()
ch1 <- 3 // 這裡會阻塞,直到上面的 goroutine 從 ch1 接收數據
// 由於上面的 goroutine 有2秒延遲,這里會等待約2秒
ch2 <- "name"
ch3 <- "hi"
fmt.Println(<-ch1) // 3
fmt.Println(<-ch2) // "name"
fmt.Println(<-ch3) // "hi"
}
顯示如下:
1
3
name
hi
- 範例&說明
package main
import "fmt"
func main() {
// 創建通道
c := make(chan int)
//併發執行
go ifunc(c)
for i := 1; i <= 10; i++{
// 將數據i發送給通道
c <- i
}
// 通道ifunc結束讀取數據
c<- 100
//等於ifunc結束
fmt.Println(<-c)
}
func ifunc(c chan int) {
// 阻塞等待數據
for {
//從通道獲取一個數據
data := <- c
// 當取得的數據為100則結束阻塞
if data == 100{
break
}
// 輸出數據
fmt.Println(data)
}
// 通知main結束
c<-0
}
output:
1
2
3
4
5
6
7
8
9
10
0
補充:
可以在
fmt.Println(<-c)與fmt.Println(data)之前,print隨便一個數值,
就可以看到,輸出模式取決於 Go 調度器如何在這兩個 goroutine 之間切換
select
select 是 Go 語言中的一個控制結構,專門用於處理多個通道操作。它的主要用途是同時等待多個通道的操作(發送或接收),並在其中一個操作可以進行時執行對應的代碼。
基本語法
select {
case <-chan1:
// 從 chan1 接收到數據時執行
case chan2 <- value:
// 向 chan2 發送數據成功時執行
case x := <-chan3:
// 從 chan3 接收到數據並賦值給 x 時執行
default:
// 沒有通道操作就緒時執行(非阻塞模式)
}
package main
import (
"fmt"
"time"
)
func main() {
// 創建兩個通道
ch1 := make(chan string)
ch2 := make(chan string)
// 在一個 goroutine 中,1秒後發送數據到 ch1
go func() {
time.Sleep(1 * time.Second)
ch1 <- "來自通道1的數據"
}()
go func() {
time.Sleep(1 * time.Second)
ch1 <- "來自通道1-1的數據"
}()
// 在另一個 goroutine 中,2秒後發送數據到 ch2
go func() {
time.Sleep(2 * time.Second)
ch2 <- "來自通道2的數據"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "來自通道2-1的數據"
}()
// 使用 select 監聽兩個通道
for i := 0; i < 4; i++ {
select {
case msg1 := <-ch1:
fmt.Println("接收到:", msg1)
case msg2 := <-ch2:
fmt.Println("接收到:", msg2)
case <-time.After(3 * time.Second):
fmt.Println("操作超時")
}
}
}
output:
接收到: 來自通道1-1的數據
接收到: 來自通道1的數據
接收到: 來自通道2的數據
接收到: 來自通道2-1的數據