Go 语言的 channel
channel
Go 语言的 channel(通道)是 Go 并发编程中的核心概念之一,
它提供了一种线程安全的方式来在 goroutine 之间传递数据。
通过 channel,可以在不同的 goroutine 之间进行通信和同步。
通道是 Go 的并发模型中的核心工具之一,它使用了“通信顺序进程”(CSP,Communicating Sequential Processes)模型,
允许不同的任务(goroutine)以同步和异步的方式交换数据。
- channel 的基本特性
类型安全:channel 的类型在定义时是固定的,一个 channel 只能传输一种类型的数据。
同步通信:如果一个 goroutine 发送数据到 channel,而另一个 goroutine 没有准备好接收数据,发送会被阻塞。
同样,接收者会在没有数据时被阻塞。
无缓冲(默认)和 有缓冲:channel 可以是无缓冲的(默认)或有缓冲的。
无缓冲 channel 会强制发送和接收操作是同步的;有缓冲 channel 则允许异步传输数据,直到缓冲区满。
- 创建和使用 Channel
3.1 创建 Channel
Go 使用 make 函数来创建 channel,并通过指定类型来确定 channel 传递的数据类型。你可以选择是否指定缓冲区大小。
无缓冲 Channel(默认)
ch := make(chan int) // 创建一个传输整数的无缓冲 channel
有缓冲 Channel(指定缓冲区大小)
ch := make(chan int, 2) // 创建一个传输整数的缓冲区大小为 2 的 channel
有缓冲的 channel 可以存储一定数量的数据,当缓冲区满时,发送操作将被阻塞,直到有数据被接收。
3.2 发送和接收数据
发送数据:
使用 <- 操作符将数据发送到 channel 中。
ch <- 42 // 将值 42 发送到 channel ch 中
接收数据:
使用 <- 操作符从 channel 中接收数据。
value := <-ch // 从 channel ch 中接收数据,并赋值给变量 value
- 通道的阻塞行为
channel 的操作是阻塞的,意思是:
如果发送者没有接收者准备好,发送操作会被阻塞。
如果接收者没有数据,接收操作会被阻塞。
func main() {ch := make(chan int) // 创建无缓冲 channelgo func() {fmt.Println("Sending data to channel...")ch <- 42 // 阻塞,直到主 goroutine 准备好接收fmt.Println("Data sent!")}()fmt.Println("Receiving data from channel...")value := <-ch // 接收数据,阻塞直到数据可用fmt.Println("Received:", value)
}
Receiving data from channel...
Sending data to channel...
Data sent!
Received: 42
- 使用有缓冲的 Channel
有缓冲的 channel 允许异步发送数据。
发送者可以在缓冲区满之前将数据发送到 channel,而接收者则可以在稍后的时候接收数据。
package mainimport "fmt"func main() {ch := make(chan int, 2) // 创建一个缓冲区大小为 2 的 channelch <- 1 // 发送数据到 channel,非阻塞ch <- 2 // 发送数据到 channel,非阻塞fmt.Println("Received:", <-ch) // 接收数据fmt.Println("Received:", <-ch) // 接收数据
}
Received: 1
Received: 2
- close 和 range 用法
关闭 Channel:当你不再需要发送数据时,可以通过 close 函数关闭 channel,
通知接收方没有更多的数据会被发送。关闭后的 channel 可以继续接收数据,但无法再发送数据。
package mainimport "fmt"func main() {ch := make(chan int, 3) // 创建有缓冲的 channelgo func() {for i := 1; i <= 3; i++ {ch <- i}close(ch) // 发送完所有数据后关闭 channel}()for value := range ch { // 遍历 channel,直到它关闭fmt.Println(value)}
}
- select 用法
select 语句类似于 switch,但它用于选择多个 channel 操作中的一个。它可以同时等待多个 channel 的事件发生,并在其中一个 channel 可用时执行相应的操作。
package mainimport "fmt"func main() {ch1 := make(chan string)ch2 := make(chan string)go func() {ch1 <- "Hello from channel 1"}()go func() {ch2 <- "Hello from channel 2"}()select {case msg1 := <-ch1:fmt.Println("Received:", msg1)case msg2 := <-ch2:fmt.Println("Received:", msg2)}
}
select 会等待 ch1 或 ch2 中的任何一个 channel 的消息。当其中一个 channel 收到消息时,相应的 case 就会被执行。